From a680abd246afb6dff8dc87d0c1de723119375e17 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Fri, 18 Nov 2016 13:39:50 +0100 Subject: [PATCH 001/132] java-lib: create a library with common POJOs Introduce a library for Java micro services manipulating common Reactivity POJOs. The fundamental class defined in this commit are ReactivityEntity, which represents a base class for any entity and Event which allows to generate a Reactivity event wrapping an entity. --- core/java-lib/pom.xml | 46 ++++++ .../reactivity/core/lib/ReactivityEntity.java | 101 +++++++++++++ .../io/reactivity/core/lib/event/Event.java | 138 ++++++++++++++++++ .../core/lib/event/package-info.java | 27 ++++ .../io/reactivity/core/lib/package-info.java | 27 ++++ 5 files changed, 339 insertions(+) create mode 100644 core/java-lib/pom.xml create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/ReactivityEntity.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java diff --git a/core/java-lib/pom.xml b/core/java-lib/pom.xml new file mode 100644 index 0000000..d118623 --- /dev/null +++ b/core/java-lib/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + io.reactivity.core + java-lib + 0.1.0-SNAPSHOT + jar + + Reactiviy Java Library + Library containing common dependencies between Java Microservices + + + UTF-8 + UTF-8 + 1.8 + 2.8.5 + + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/ReactivityEntity.java b/core/java-lib/src/main/java/io/reactivity/core/lib/ReactivityEntity.java new file mode 100644 index 0000000..95e3a3a --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/ReactivityEntity.java @@ -0,0 +1,101 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + *

+ * Representation of the common definition that all entities stored in Reactivity must have. An entity is always + * identified with a creation date, a version that correspond to a specific code logic and an ID. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class ReactivityEntity { + + /** + * The version of the entity. + */ + @JsonIgnore + private final String version; + + /** + * The entity ID. + */ + @JsonIgnore + private final String id; + + /** + * The creation date. + */ + @JsonIgnore + private final long updated; + + /** + *

+ * Builds a new instance. + *

+ * + * @param version the entity version + * @param id the entity ID + * @param updated when the entity was created/updated + */ + protected ReactivityEntity(final String version, final String id, final long updated) { + this.version = version; + this.id = id; + this.updated = updated; + } + + /** + *

+ * Gets the entity version. + *

+ * + * @return the version + */ + public String getVersion() { + return version; + } + + /** + *

+ * Gets the entity ID. + *

+ * + * @return the ID + */ + public String getId() { + return id; + } + + /** + *

+ * Gets the creation or last updated date. + *

+ * + * @return the timestamp + */ + public long getUpdated() { + return updated; + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java new file mode 100644 index 0000000..d637e3a --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java @@ -0,0 +1,138 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import io.reactivity.core.lib.ReactivityEntity; + +/** + *

+ * Representation of a reactivity event. An event is identified by the ID of its data (which must be a + * {@link io.reactivity.core.lib.ReactivityEntity}), and the event type. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + * @param + */ +public class Event { + + /** + * The version. + */ + private final String version; + + /** + * The event ID. + */ + private final String id; + + /** + * The event type. + */ + private final String event; + + /** + * When the data was created/updated. + */ + private final long updated; + + /** + * The data payload. + */ + private final T data; + + /** + *

+ * Builds a new instance. + *

+ * + * @param event the event type + * @param data the data payload + */ + public Event(final String event, final T data) { + this.id = data.getId(); + this.event = event; + this.updated = data.getUpdated(); + this.data = data; + this.version = data.getVersion(); + } + + /** + *

+ * Gets the version. + *

+ * + * @return the data version + */ + public String getVersion() { + return version; + } + + /** + *

+ * Gets the ID. + *

+ * + * @return the data ID + */ + public String getId() { + return id; + } + + /** + *

+ * Gets the event type. + *

+ * + * @return the event type + */ + public String getEvent() { + return event; + } + + /** + *

+ * Gets the creation or last updated date. + *

+ * + * @return the data timestamp + */ + public long getUpdated() { + return updated; + } + + /** + *

+ * Gets the data. + *

+ * + * @return the payload + */ + public T getData() { + return data; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("event type: %s / id: %s", event, id); + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java new file mode 100644 index 0000000..b1f6b36 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + *

+ * This package defines several events that can be generated inside Reactivity platform. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +package io.reactivity.core.lib.event; \ No newline at end of file diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java b/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java new file mode 100644 index 0000000..883a0e9 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + *

+ * Base package for Java library defining common fundamentals objects. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +package io.reactivity.core.lib; \ No newline at end of file From bea35105e94e691b02f659d3dd8183f1d4e59d41 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Fri, 18 Nov 2016 13:43:51 +0100 Subject: [PATCH 002/132] list-view: create event POJOs for list view Add the POJOs that allow to manage a list view with a type defined in an ViewType enumeration. A list view allows to manage artifacts and belongs to an organization. --- .../java/io/reactivity/core/lib/ViewType.java | 34 +++++ .../reactivity/core/lib/event/Artifact.java | 89 +++++++++++ .../core/lib/event/ArtifactView.java | 142 ++++++++++++++++++ .../reactivity/core/lib/event/EventType.java | 74 +++++++++ .../io/reactivity/core/lib/event/Member.java | 79 ++++++++++ .../core/lib/event/Organization.java | 107 +++++++++++++ .../io/reactivity/core/lib/event/Period.java | 115 ++++++++++++++ 7 files changed, 640 insertions(+) create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/Artifact.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/ArtifactView.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/Member.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/Organization.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java b/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java new file mode 100644 index 0000000..d7c7cec --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java @@ -0,0 +1,34 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib; + +/** + *

+ * Enumeration of core supported types of artifacts views. + *

+ * + * @author Guillaume DROUET + */ +public enum ViewType { + + /** + * A list view. + */ + LIST +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Artifact.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Artifact.java new file mode 100644 index 0000000..20c9482 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Artifact.java @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import io.reactivity.core.lib.ReactivityEntity; + +import java.util.List; +import java.util.Map; + +/** + *

+ * A representation of an artifact that is streamed across the Reactivity platform. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +public class Artifact extends ReactivityEntity { + + /** + * A list of views containing this artifact. + */ + private final List views; + + /** + * All categories defined in that artifact. + */ + private final Map categories; + + /** + *

+ * Builds a new instance. + *

+ * + * @param version the entity version + * @param id the artifact ID + * @param timestamp when the artifact was created + * @param views the views containing the artifact + * @param categories the categories + */ + public Artifact(final String version, + final String id, + final long timestamp, + final List views, + final Map categories) { + super(version, id, timestamp); + + this.views = views; + this.categories = categories; + } + + /** + *

+ * Gets the views. + *

+ * + * @return the artifact views + */ + public List getViews() { + return views; + } + + /** + *

+ * Gets the categories. + *

+ * + * @return the artifact categories + */ + public Map getCategories() { + return categories; + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/ArtifactView.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/ArtifactView.java new file mode 100644 index 0000000..dcadc98 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/ArtifactView.java @@ -0,0 +1,142 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import io.reactivity.core.lib.ReactivityEntity; + +/** + *

+ * A representation of a view allowing to query a set of artifacts from the associated organization. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +public class ArtifactView extends ReactivityEntity { + + /** + * The organization ID. + */ + private final String organization; + + /** + * The view name. + */ + private final String name; + + /** + * The period that covers the artifacts. + */ + private final Period period; + + /** + * The type of view. + */ + private final String type; + + /** + *

+ * Builds a new instance by copy and a specific period. + *

+ * + * @param other the view to copy + * @param period the period covering the selected artifacts + */ + public ArtifactView(final ArtifactView other, + final Period period) { + super(other.getVersion(), other.getId(), other.getUpdated()); + + this.organization = other.getOrganization(); + this.name = other.getType(); + this.period = period; + this.type = other.getType(); + } + + /** + *

+ * Builds a new instance. + *

+ * + * @param version the entity version + * @param id the view ID + * @param timestamp when the view was created + * @param organization the organization ID + * @param name the view name + * @param period the period covering the selected artifacts + * @param type the view type + */ + public ArtifactView(final String version, + final String id, + final long timestamp, + final String organization, + final String name, + final Period period, + final String type) { + super(version, id, timestamp); + + this.organization = organization; + this.name = name; + this.period = period; + this.type = type; + } + + /** + *

+ * Gets the organization ID. + *

+ * + * @return the view's organization + */ + public String getOrganization() { + return organization; + } + + /** + *

+ * Gets the name. + *

+ * + * @return the view's name + */ + public String getName() { + return name; + } + + /** + *

+ * Gets the period. + *

+ * + * @return the view's period + */ + public Period getPeriod() { + return period; + } + + /** + *

+ * Gets the type. + *

+ * + * @return the view type + */ + public String getType() { + return type; + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java new file mode 100644 index 0000000..bed0829 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import io.reactivity.core.lib.ReactivityEntity; + +/** + *

+ * Enumeration of all supported core event types in the {@link Event#event} field. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +public enum EventType { + + /** + * Read artifact event. + */ + READ_ARTIFACT, + + /** + * Read view event. + */ + READ_VIEW, + + /** + * Read organization event. + */ + READ_ORGANIZATION; + + /** + *

+ * Builds a new event based on a free event type and a data payload. + *

+ * + * @param eventType the event type + * @param data the data + * @param the payload type that must be a {@link ReactivityEntity} + * @return the event + */ + public static Event newEvent(final String eventType, final T data) { + return new Event<>(eventType, data); + } + + /** + *

+ * Creates a new {@link Event} with a type corresponding to this enumeration. + *

+ * + * @param data the data payload + * @param the payload type that must be a {@link ReactivityEntity} + * @return the event + */ + public Event newEvent(final T data) { + return newEvent(name(), data); + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Member.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Member.java new file mode 100644 index 0000000..fbb5a40 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Member.java @@ -0,0 +1,79 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + *

+ * Representation of an organization member. The details about the user can be found with an ID and a role specifies + * the rights of this user inside the organization. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Member { + + /** + * The user ID. + */ + private String id; + + /** + * The user role within the organization. + */ + private String role; + + /** + *

+ * Builds a new instance. + *

+ * + * @param id the user ID + * @param role the user role + */ + public Member(final String id, final String role) { + this.id = id; + this.role = role; + } + + /** + *

+ * Gets the ID. + *

+ * + * @return the user ID + */ + public String getId() { + return id; + } + + /** + *

+ * Gets the role. + *

+ * + * @return the user role + */ + public String getRole() { + return role; + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Organization.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Organization.java new file mode 100644 index 0000000..55283b8 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Organization.java @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import io.reactivity.core.lib.ReactivityEntity; + +import java.util.List; + +/** + *

+ * A representation of an organization that is streamed across the Reactivity platform. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +public class Organization extends ReactivityEntity { + + /** + * The organization name. + */ + private String name; + + /** + * The organization picture in base 64. + */ + private String picture; + + /** + * A list of members of the organization. + */ + private List members; + + /** + *

+ * Builds a new instance. + *

+ * + * @param version the organization structure version + * @param id the organization ID + * @param timestamp when the organization was created + * @param name the organization name + * @param picture the organization picture + * @param members the organization members + */ + public Organization(final String version, + final String id, + final long timestamp, + final String name, + final String picture, + final List members) { + super(version, id, timestamp); + + this.name = name; + this.picture = picture; + this.members = members; + } + + /** + *

+ * Gets the name. + *

+ * + * @return the organization name + */ + public String getName() { + return name; + } + + /** + *

+ * Gets the picture. + *

+ * + * @return the organization picture + */ + public String getPicture() { + return picture; + } + + /** + *

+ * Gets the members. + *

+ * + * @return the organization members + */ + public List getMembers() { + return members; + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java new file mode 100644 index 0000000..38c61f9 --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + *

+ * A period of time defined by a {@link ArtifactView}. Artifacts can be selected by defining a range of timestamps. + * One of the two timestamps (the first corresponding to the oldest artifact and the second to the newest artifact) + * can be {@code null}. A limit of returned artifact can be specified. Otherwise, artifacts are selected without limit. + * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Period { + + /** + * The artifacts are selected from this timestamp. + */ + private Long from; + + /** + * The artifacts are selected until this timestamp. + */ + private Long to; + + /** + * A limit of returned artifact can be specified. + */ + private Integer limit; + + /** + * A particular category that will be compared to the timestamp instead of the last update date. + */ + private String category; + + /** + *

+ * Builds a new instance with a bounded period. + *

+ * + * @param from starting timestamp + * @param to ending timestamp + * @param limit the maximum number of returned artifacts + * @param category the timestamp category + */ + public Period(final Long from, final Long to, final Integer limit, final String category) { + this.from = from; + this.to = to; + this.limit = limit; + this.category = category; + } + + /** + *

+ * Gets the {@code from} timestamp. + *

+ * + * @return the when the artifacts start + */ + public Long getFrom() { + return from; + } + + /** + *

+ * Gets the {@code to} timestamp. + *

+ * + * @return the when the artifacts end + */ + public Long getTo() { + return to; + } + + /** + *

+ * Gets the maximum number artifacts. + *

+ * + * @return the maximum number of artifacts + */ + public Integer getLimit() { + return limit; + } + + /** + *

+ * Gets the from timestamp category. + *

+ * + * @return the category to compare with timestamps + */ + public String getCategory() { + return category; + } +} From de6e1ebcf907325b0e9f47b105e978a6d810ca52 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Fri, 18 Nov 2016 21:36:36 +0100 Subject: [PATCH 003/132] list-view: add API to retrieve artifacts from a list view First endpoints returning a Flux typed with Event are introduced by this commit to: - retrieve a mocked organization with a single list view - subscribe to this mocked organization and receive associated view with its corresponding artifacts - retrieve some additional artifacts matching a specific period of time The view selects the 100 newest artifacts when the user subscribes to the organization. A mocked index.html shows how this web service can be called and then retrieve older artifacts based on the last received events. The endpoints are currently not suspending a SSE connection because Reactor currently broadcast any event to all connections, which is not what we want for this use case. It's plan in a future release of Reactor to provide such capability. Flux are configured to fallback to an Event publisher in case of exception or timeout. This mechanism needs to be improved with retry capabilities, including replica usage for couchbase, that meet circuit-breaking capabilities. There's still a random issue (under investigation) where no artifacts is returned by N1QL query in couchbase the first time a web service is called. Also, log4j2 is raising an exception when called by Reactor through SLF4J and that's why it's not present in this commit. --- core/broadcaster/pom.xml | 158 ++++++++++++++++++ .../core/broadcaster/Application.java | 42 +++++ .../broadcaster/config/CouchbaseConfig.java | 60 +++++++ .../core/broadcaster/config/package-info.java | 21 +++ .../core/broadcaster/package-info.java | 21 +++ .../repository/ReactivityRepository.java | 88 ++++++++++ .../couchbase/ArtifactViewQuery.java | 45 +++++ .../couchbase/ArtifactViewQueryFactory.java | 58 +++++++ .../CouchbaseReactvityRepository.java | 83 +++++++++ .../couchbase/ListArtifactViewQuery.java | 115 +++++++++++++ .../MockCouchbaseReactivityRepository.java | 126 ++++++++++++++ .../repository/couchbase/package-info.java | 21 +++ .../broadcaster/repository/package-info.java | 21 +++ .../broadcaster/service/EventService.java | 147 ++++++++++++++++ .../broadcaster/service/FluxDecorator.java | 86 ++++++++++ .../broadcaster/service/package-info.java | 21 +++ .../core/broadcaster/web/EventController.java | 94 +++++++++++ .../core/broadcaster/web/MockController.java | 45 +++++ .../core/broadcaster/web/package-info.java | 21 +++ .../main/resources/mocks/Squirrel-logo.png | Bin 0 -> 28002 bytes .../src/main/resources/mocks/index.html | 81 +++++++++ .../src/main/resources/mocks/react-3.png | Bin 0 -> 5425 bytes .../core/broadcaster/RepositoryTest.java | 87 ++++++++++ core/java-lib/pom.xml | 18 ++ .../io/reactivity/core/lib/event/Error.java | 105 ++++++++++++ .../reactivity/core/lib/event/EventType.java | 7 +- .../core/lib/event/package-info.java | 5 - .../io/reactivity/core/lib/package-info.java | 5 - .../core/lib/application.properties | 1 + 29 files changed, 1571 insertions(+), 11 deletions(-) create mode 100644 core/broadcaster/pom.xml create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/Application.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/package-info.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/package-info.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQueryFactory.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/package-info.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/package-info.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/package-info.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java create mode 100644 core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/package-info.java create mode 100644 core/broadcaster/src/main/resources/mocks/Squirrel-logo.png create mode 100644 core/broadcaster/src/main/resources/mocks/index.html create mode 100644 core/broadcaster/src/main/resources/mocks/react-3.png create mode 100644 core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java create mode 100644 core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java create mode 100644 core/java-lib/src/main/resources/io/reactivity/core/lib/application.properties diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml new file mode 100644 index 0000000..557b08a --- /dev/null +++ b/core/broadcaster/pom.xml @@ -0,0 +1,158 @@ + + + 4.0.0 + + io.reactivity.core + broadcaster + 0.1.0-SNAPSHOT + jar + + Reactivity core broadcaster + Microservice holding client SSE connections and broadcasting events + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.BUILD-SNAPSHOT + + + + + UTF-8 + UTF-8 + 1.8 + 2.0.0.DATACMNS-836-SNAPSHOT + 2.3.4 + 1.1.9 + 1.1.1 + 3.0.3.RELEASE + 0.5.2.RELEASE + 0.1.0.BUILD-SNAPSHOT + 0.6.0.BUILD-SNAPSHOT + + + + + + + org.springframework.data + spring-data-commons + ${spring-data.version} + + + com.couchbase.client + java-client + ${couchbase-client.version} + + + io.reactivex + rxjava + ${rxjava.version} + + + io.reactivex + rxjava-reactive-streams + ${rxjava-reactive-streams.version} + + + io.projectreactor + reactive-streams-commons + ${reactive-streams-common.version} + + + + io.reactivity.core + java-lib + ${project.version} + + + + + io.projectreactor + reactor-core + ${reactor-core.version} + + + io.projectreactor.ipc + reactor-netty + ${reactor-netty.version} + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + ${spring-web-reactive.version} + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + + jfrog-snapshots + JFrog Snapshots + https://oss.jfrog.org/libs-snapshot + + true + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/Application.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/Application.java new file mode 100644 index 0000000..0556aed --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/Application.java @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.reactive.config.WebReactiveConfigurationSupport; + +/** + * Bootstrap class. + * + * @author Guillaume DROUET + */ +@SpringBootApplication +public class Application extends WebReactiveConfigurationSupport { + + /** + * Main. + * + * @param args ignored args + * @throws Exception if spring fails + */ + public static void main(final String[] args) throws Exception { + SpringApplication.run(Application.class, args); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java new file mode 100644 index 0000000..082b7b7 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.config; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.CouchbaseCluster; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + *

+ * This configuration initializes couchbase connection. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@Configuration +@ConfigurationProperties(prefix = "reactivity.couchbase") +public class CouchbaseConfig { + + private String[] nodes = new String[] { "127.0.0.1" } ; + + /** + *

+ * The application won't be able to use this bucket until an index is created. + *

+ * + * @return the {@code Bucket} + */ + @Bean + Bucket sync() { + final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); + final Bucket bucket = cluster.openBucket("artifact"); + bucket.bucketManager().createN1qlPrimaryIndex(true, false); + + return bucket; + } + + /** + *

+ * Sets the couchbase nodes with a comma-separated list of IPs. + *

+ * + * @param nodes the bootstrap nodes + */ + public void setNodes(final String[] nodes) { + this.nodes = nodes; + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/package-info.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/package-info.java new file mode 100644 index 0000000..6b4466c --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/package-info.java @@ -0,0 +1,21 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Main package for the broadcaster micro service. + */ +package io.reactivity.core.broadcaster.config; diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/package-info.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/package-info.java new file mode 100644 index 0000000..46f1070 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/package-info.java @@ -0,0 +1,21 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Broadcaster configuration package. + */ +package io.reactivity.core.broadcaster; diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java new file mode 100644 index 0000000..2dc7eff --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java @@ -0,0 +1,88 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.repository; + +import io.reactivity.core.lib.event.Artifact; +import io.reactivity.core.lib.event.Organization; +import io.reactivity.core.lib.event.ArtifactView; +import org.reactivestreams.Publisher; + +import java.util.function.Function; + +/** + *

+ * Data repository for Reactivity. Fundamental objects are retrieved through this repository. + * All methods return a {@code Publisher} that sends data are available after a query execution. + * The publisher type is specified by a {@code Function} provided through the parameters that acts as a mapper from + * the supplied particular {@link io.reactivity.core.lib.ReactivityEntity}. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +public interface ReactivityRepository { + + /** + *

+ * Gets all the organizations associated to the given member. + *

+ * + * @param memberId the member ID + * @param mapper the map function to the desired returned type + * @param the desired returned type + * @return a publisher where source is the retrieved organizations + */ + Publisher findOrganizationsWithMember(String memberId, Function mapper); + + /** + *

+ * Gets all the views associated to the given organization. + *

+ * + * @param organizationId the organization ID + * @param mapper the map function to the desired returned type + * @param the desired returned type + * @return a publisher where source is the retrieved views + */ + Publisher findViewsFromOrganization(String organizationId, Function mapper); + + /** + *

+ * Gets all the artifacts associated to the given view. + *

+ * + * @param view the view + * @param mapper the map function to the desired returned type + * @param the desired returned type + * @return a publisher where source is the retrieved artifacts + */ + Publisher findArtifactFromView(ArtifactView view, Function mapper); + + /** + *

+ * Finds a view with a particular ID. + *

+ * + * @param id the view ID + * @param mapper the map function to the desired returned type + * @param the desired returned type + * @return a publisher where source is the retrieved view + */ + Publisher findViewById(String id, Function mapper); +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java new file mode 100644 index 0000000..58bb8f1 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java @@ -0,0 +1,45 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.repository.couchbase; + +import com.couchbase.client.java.AsyncBucket; +import io.reactivity.core.lib.event.Artifact; +import rx.Observable; + +/** + *

+ * This interface is able to execute a query based on a particular internal + * {@link io.reactivity.core.lib.event.ArtifactView} to retrieve some artifacts. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +interface ArtifactViewQuery { + + /** + *

+ * Executes the query. + *

+ * + * @param bucket the bucket to be used + * @return the result artifacts + */ + Observable query(AsyncBucket bucket); +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQueryFactory.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQueryFactory.java new file mode 100644 index 0000000..09a2b09 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQueryFactory.java @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.repository.couchbase; + +import io.reactivity.core.lib.ViewType; +import io.reactivity.core.lib.event.ArtifactView; + +/** + *

+ * This factory resolves the {@link ViewType} of an {@link ArtifactView} and builds the proper {@link ArtifactViewQuery} + * ready to execute a query for this view. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +enum ArtifactViewQueryFactory { + + /** + * Singleton. + */ + INSTANCE; + + /** + *

+ * Creates a {@link ArtifactViewQuery} from the given {@link ArtifactView}. + *

+ * + * @param view the view + * @return the view query + */ + ArtifactViewQuery create(final ArtifactView view) { + final ViewType type = ViewType.valueOf(view.getType().toUpperCase()); + + switch (type) { + case LIST: + return new ListArtifactViewQuery(view); + default: + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java new file mode 100644 index 0000000..a45bcb0 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java @@ -0,0 +1,83 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.repository.couchbase; + +import com.couchbase.client.java.AsyncBucket; +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.query.AsyncN1qlQueryResult; +import io.reactivity.core.broadcaster.repository.ReactivityRepository; +import io.reactivity.core.lib.event.Artifact; +import io.reactivity.core.lib.event.ArtifactView; +import org.reactivestreams.Publisher; +import rx.Observable; +import rx.RxReactiveStreams; + +import java.io.IOException; + +import java.util.function.Function; + +/** + *

+ * This repository performs operations on {@link Artifact} documents with {@code RxJava} support. + *

+ */ +abstract class CouchbaseReactvityRepository implements ReactivityRepository { + + /** + * The bucket. + */ + private final AsyncBucket bucket; + + /** + *

+ * Builds a new repository. + *

+ * + * @param bucket the bucket + * @throws IOException if the repository is not able to initialize the views + */ + protected CouchbaseReactvityRepository(final Bucket bucket) throws IOException { + this.bucket = bucket.async(); + } + + /** + * {@inheritDoc} + */ + @Override + public Publisher findArtifactFromView(final ArtifactView view, final Function mapper) { + final ArtifactViewQuery artifactViewQuery = ArtifactViewQueryFactory.INSTANCE.create(view); + + return RxReactiveStreams.toPublisher(artifactViewQuery.query(bucket).map(mapper::apply)); + } + + /** + *

+ * Generates an error for the given result. + *

+ * + * @param asyncQueryResult the result + * @return the observable error + */ + static Observable error(final AsyncN1qlQueryResult asyncQueryResult) { + return asyncQueryResult.errors().flatMap( + jsonErrors -> Observable.error( + new IllegalStateException( + jsonErrors.getInt("code") + ": " + jsonErrors.getString("msg")))); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java new file mode 100644 index 0000000..346be57 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.repository.couchbase; + +import com.couchbase.client.java.AsyncBucket; +import com.couchbase.client.java.document.json.JsonObject; +import com.couchbase.client.java.query.*; +import com.couchbase.client.java.query.dsl.Expression; +import com.couchbase.client.java.query.dsl.Sort; +import com.couchbase.client.java.query.dsl.path.LimitPath; +import io.reactivity.core.lib.event.Artifact; +import io.reactivity.core.lib.event.ArtifactView; +import io.reactivity.core.lib.event.Period; +import rx.Observable; + +import java.util.Collections; + +/** + *

+ * This {@link ArtifactViewQuery} supports queries for {@link ArtifactView} of type + * {@link io.reactivity.core.lib.ViewType#LIST}. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +class ListArtifactViewQuery implements ArtifactViewQuery { + + /** + * Fields in the SELECT clause of the query. + */ + private static final String FIELDS = "version, meta(artifact).id, categories, updated"; + + /** + * The view to use. + */ + private final ArtifactView view; + + /** + *

+ * Builds a new instance. + *

+ * + * @param view the view corresponding to the executed query + */ + ListArtifactViewQuery(final ArtifactView view) { + this.view = view; + } + + /** + * {@inheritDoc} + */ + @Override + public Observable query(final AsyncBucket bucket) { + + // Prepare the statement and parameters + final JsonObject params = JsonObject.create().put("organization", view.getOrganization()); + final Period period = view.getPeriod(); + final String timestampField = period.getCategory() != null ? "categories." + period.getCategory() : "updated"; + + Expression expression = Expression.x("organization").eq(Expression.x("$organization")); + + if (period.getFrom() != null) { + expression = expression.and(Expression.x(timestampField).gte(period.getFrom())); + } + + if (period.getTo() != null) { + expression = expression.and(Expression.x(timestampField).lte(period.getTo())); + } + + final LimitPath limitPath = Select.select(FIELDS).from("artifact").where(expression).orderBy(Sort.desc(timestampField)); + final Statement statement = period.getLimit() != null ? limitPath.limit(period.getLimit()) : limitPath; + + // Build the query + final ParameterizedN1qlQuery query = N1qlQuery.parameterized(statement, params); + + // Execute and map to an Observable of Artifact + return bucket.query(query).flatMap(result -> result.parseSuccess() ? + // Map each internal JSON object to an Artifact and handles statement failure + result.rows().map(this::toArtifact) : CouchbaseReactvityRepository.error(result)); + } + + /** + *

+ * Creates an artifact from the given row result. + *

+ * + * @param row the row result + * @return the mapped artifact + */ + private Artifact toArtifact(final AsyncN1qlQueryRow row) { + final JsonObject o = JsonObject.class.cast(row.value()); + return new Artifact(o.getString("version"), + o.getString("id"), + o.getLong("updated"), + Collections.singletonList(view.getId()), + o.getObject("categories").toMap()); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java new file mode 100644 index 0000000..3474680 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java @@ -0,0 +1,126 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.repository.couchbase; + +import com.couchbase.client.java.Bucket; +import io.reactivity.core.lib.ViewType; +import io.reactivity.core.lib.event.ArtifactView; +import io.reactivity.core.lib.event.Member; +import io.reactivity.core.lib.event.Organization; +import io.reactivity.core.lib.event.Period; +import org.reactivestreams.Publisher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Repository; +import org.springframework.util.FileCopyUtils; +import rx.Observable; +import rx.RxReactiveStreams; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Base64; +import java.util.UUID; +import java.util.function.Function; + +/** + *

+ * A repository providing mocked data for missing methods implementation in {@link CouchbaseReactvityRepository}. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@Repository +public class MockCouchbaseReactivityRepository extends CouchbaseReactvityRepository { + + /** + *

+ * Builds a new repository. + *

+ * + * @param bucket the bucket + * @throws IOException if the repository is not able to initialize the views + */ + @Autowired + public MockCouchbaseReactivityRepository(final Bucket bucket) throws IOException { + super(bucket); + } + + /** + * {@inheritDoc} + */ + @Override + public Publisher findViewById(final String id, final Function mapper) { + return findViewsFromOrganization("Organization/1", mapper); + } + + /** + * {@inheritDoc} + */ + @Override + public Publisher findViewsFromOrganization(final String organizationId, final Function mapper) { + return RxReactiveStreams.toPublisher( + Observable.fromCallable(() -> new ArtifactView( + "0.1.0-SNAPSHOT", + nextMockedId(), + nexMockTimestamp(), + organizationId, + "Last artifacts created in your organization", + new Period(null, null, 100, null), + ViewType.LIST.name())) + .map(mapper::apply)); + } + + /** + * {@inheritDoc} + */ + @Override + public Publisher findOrganizationsWithMember(final String memberId, final Function mapper) { + return RxReactiveStreams.toPublisher(Observable.fromCallable(() -> new Organization( + "0.1.0-SNAPSHOT", + "Organization/1", + nexMockTimestamp(), + "Reactivity", + loadMockedImage("react-3.png"), + Arrays.asList(new Member("User/1", "ADMIN")))).map((view) -> mapper.apply(view))); + } + + private String nextMockedId() { + return UUID.randomUUID().toString(); + } + + private long nexMockTimestamp() { + return System.currentTimeMillis(); + } + + private String loadMockedImage(final String mockName) { + final Resource cpr = new ClassPathResource("mocks/" + mockName); + final byte[] picture; + + try (final InputStream is = cpr.getInputStream()) { + picture = FileCopyUtils.copyToByteArray(is); + } catch (IOException ioe) { + throw new IllegalStateException(ioe); + } + + return Base64.getEncoder().encodeToString(picture); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/package-info.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/package-info.java new file mode 100644 index 0000000..713c0f1 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/package-info.java @@ -0,0 +1,21 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Broadcaster couchbase repository package. + */ +package io.reactivity.core.broadcaster.repository.couchbase; diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/package-info.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/package-info.java new file mode 100644 index 0000000..46c23eb --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/package-info.java @@ -0,0 +1,21 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Broadcaster repository package. + */ +package io.reactivity.core.broadcaster.repository; diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java new file mode 100644 index 0000000..698f402 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java @@ -0,0 +1,147 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.service; + +import io.reactivity.core.broadcaster.repository.ReactivityRepository; +import io.reactivity.core.lib.ReactivityEntity; +import io.reactivity.core.lib.event.ArtifactView; +import io.reactivity.core.lib.event.Event; +import io.reactivity.core.lib.event.EventType; +import io.reactivity.core.lib.event.Organization; +import io.reactivity.core.lib.event.Period; +import org.reactivestreams.Publisher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + *

+ * Reactivity service providing streams of events available in the platform. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@Service +public class EventService { + + /** + * The repository storing the data. + */ + @Autowired + private ReactivityRepository repository; + + /** + * Flux decorator. + */ + @Autowired + private FluxDecorator fluxDecorator; + + /** + *

+ * Loads all the artifacts matching the given view but with the specified period. + *

+ * + * @param limit maximum number of returned artifacts + * @param maxAge highest possible age for an artifact + * @param viewId the view ID + * @return the event flux + */ + public Flux> loadArtifacts(final String viewId, final int limit, final long maxAge) { + final Function periodAdapter = + v -> new ArtifactView(v, new Period(v.getPeriod().getFrom(), maxAge, limit, v.getPeriod().getCategory())); + + return Flux.from(repository.findViewById(viewId, periodAdapter)) + .concatMap(v -> repository.findArtifactFromView(v, EventType.READ_ARTIFACT::newEvent)); + } + + /** + *

+ * Loads the organizations associated to the user identified with the given user ID. + *

+ * + * @param userId the user ID + * @return the organization event flux + */ + public Flux> loadOrganizations(final String userId) { + // Retrieve the organizations: member ID can be an arbitrary value as it is currently mocked + return Flux.from(repository.findOrganizationsWithMember(userId, EventType.READ_ORGANIZATION::newEvent)); + } + + /** + *

+ * Subscribes to a particular {@link Organization} and sends it to the response with its {@link ArtifactView views} + * and corresponding {@link io.reactivity.core.lib.event.Artifact artifacts}. + *

+ * + * @param organizationId the organization ID + * @return the event flux + */ + public Flux> subscribe(final String organizationId) { + return Flux.create(emitter -> { + // Complete the emitter once all flux have been completed + final AtomicInteger fluxCount = new AtomicInteger(1); + final Consumer onError = e -> emitter.complete(); + final Runnable finishFlux = () -> { + if (fluxCount.decrementAndGet() == 0) { + emitter.complete(); + } + }; + + final Consumer> emit = e -> { + fluxCount.incrementAndGet(); + emitter.next(e); + }; + + // When an organization is received... + Flux.from(repository.findOrganizationsWithMember(organizationId, o -> o)).doOnError(onError).subscribe(o -> { + // ... retrieve the associated views + final Publisher> viewsPub = + repository.findViewsFromOrganization(o.getId(), EventType.READ_VIEW::newEvent); + final Flux> views = fluxDecorator.decorate( + Flux.from(viewsPub) + .doOnError(onError) + .doOnComplete(finishFlux), "findViewsFromOrganization(" + o.getId() + ")"); + + // When a view is received ... + views.subscribe(v -> { + // ... emit it ... + emit.accept(v); + + if (v.getData() instanceof ArtifactView) { + final ArtifactView view = ArtifactView.class.cast(v.getData()); + final Publisher> artifactsPub = + repository.findArtifactFromView(view, EventType.READ_ARTIFACT::newEvent); + + // ... and retrieve the associated artifacts + fluxDecorator.decorate( + Flux.from(artifactsPub) + .doOnError(onError) + .doOnComplete(finishFlux), "findArtifactFromView(" + view + ")") + .subscribe(emitter::next); + } + }); + }); + }); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java new file mode 100644 index 0000000..e1b357c --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java @@ -0,0 +1,86 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.service; + +import io.reactivity.core.lib.ReactivityEntity; +import io.reactivity.core.lib.event.Error; +import io.reactivity.core.lib.event.Event; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import rsc.publisher.PublisherJust; + +import java.time.Duration; + +/** + *

+ * This aspect modifies the {@code Flux} returned by {@link EventService} methods in order to add a timeout and an error + * handlers in a generic way. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@Aspect +@Component +public class FluxDecorator { + + /** + * Timeout applied to {@code Flux}. + */ + private static final Duration TIMEOUT = Duration.ofSeconds(10); + + /** + *

+ * Method interceptor that decorates the returned {@code Flux} with a {@link #TIMEOUT timeout} handler, + * an {@link Error error} handler and a log name corresponding to a {@code String} representation of the + * method invocation. + *

+ * + * @param proceedingJoinPoint the join point + * @return the modified {@code Flux} + * @throws Throwable if proceed fails + */ + @Around("execution(* io.reactivity.core.broadcaster.service.EventService.*(..))") + @SuppressWarnings("unchecked") + public Flux> decorate(final ProceedingJoinPoint proceedingJoinPoint) + throws Throwable { + return decorate(Flux.class.>cast(proceedingJoinPoint.proceed()), + proceedingJoinPoint.toLongString()); + } + + /** + *

+ * Decorates the returned {@code Flux} with a {@link #TIMEOUT timeout} handler, an {@link Error error} handler and a + * log name. + *

+ * + * @param flux the {@code Flux} to decorate + * @param log the log name + * @return the decorated {@code Flux} + */ + Flux> decorate(final Flux> flux, + final String log) { + return flux.timeout(TIMEOUT, PublisherJust.fromCallable(Error::timeout)) + .switchOnError(PublisherJust.fromCallable(Error::exception)) + .log(log); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/package-info.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/package-info.java new file mode 100644 index 0000000..5c08006 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/package-info.java @@ -0,0 +1,21 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Main package for the broadcaster micro service. + */ +package io.reactivity.core.broadcaster.service; diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java new file mode 100644 index 0000000..99c656c --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java @@ -0,0 +1,94 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster.web; + +import io.reactivity.core.broadcaster.service.EventService; +import io.reactivity.core.lib.event.Event; +import io.reactivity.core.lib.event.Organization; +import io.reactivity.core.lib.ReactivityEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; + +/** + *

+ * This controller serves event streams over REST API. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@RestController +public class EventController { + + /** + * The service. + */ + @Autowired + private EventService eventService; + + /** + *

+ * Loads all the artifacts matching the given view but with the specified period. + *

+ * + * @param viewId the view ID + * @param limit maximum number of returned artifacts + * @param maxAge highest possible age for an artifact + * @return the event flux + */ + @GetMapping("/load/artifacts/{viewId}/limit/{limit}/maxage/{maxAge}") + Flux> loadArtifacts( + @PathVariable(name = "viewId") final String viewId, + @PathVariable(name = "limit") final int limit, + @PathVariable(name = "maxAge") final long maxAge) { + return eventService.loadArtifacts(viewId, limit, maxAge); + } + + /** + *

+ * Loads the organizations as specified by {@link EventService#loadOrganizations(String)}. + *

+ * + * @param sessionId the session ID + * @return the organization event flux + */ + @GetMapping("/load/organizations") + public Flux> loadOrganizations( + @CookieValue(value = "SESSION", required = false) final String sessionId) { + // Retrieve the organizations: member ID can be an arbitrary value (here the session ID) as it is currently mocked + return eventService.loadOrganizations(sessionId); + } + + /** + *

+ * Subscribes to a particular {@link Organization} as specified by {@link EventService#subscribe(String)}. + *

+ * + * @param organizationId the organization ID + * @return the event flux + */ + @GetMapping("/subscribe/{organizationId}") + Flux> subscribe(@PathVariable(name = "organizationId") final String organizationId) { + return eventService.subscribe(organizationId); + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java new file mode 100644 index 0000000..673f952 --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java @@ -0,0 +1,45 @@ +package io.reactivity.core.broadcaster.web; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.UUID; + +/** + *

+ * This controller serving a test UI. + *

+ * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@RestController +public class MockController { + + /** + *

+ * Generates a test index.html page with a new cookie to track the user session. + *

+ * + * @return the HTTP response + * @throws IOException if HTML page can be loaded + */ + @GetMapping("/") + public ResponseEntity index() throws IOException { + final String sessionId = UUID.randomUUID().toString(); + final HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.add("Set-Cookie", "SESSION=" + sessionId); + responseHeaders.set("Content-Type", "text/html"); + + try (InputStreamReader isr = new InputStreamReader(new ClassPathResource("mocks/index.html").getInputStream())) { + return new ResponseEntity<>(FileCopyUtils.copyToString(isr), responseHeaders, HttpStatus.OK); + } + } +} diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/package-info.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/package-info.java new file mode 100644 index 0000000..4b8a1ee --- /dev/null +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/package-info.java @@ -0,0 +1,21 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Main package for the broadcaster micro service. + */ +package io.reactivity.core.broadcaster.web; diff --git a/core/broadcaster/src/main/resources/mocks/Squirrel-logo.png b/core/broadcaster/src/main/resources/mocks/Squirrel-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..698d8bdf37d85fae9de57bdc3158db7761d7f9ec GIT binary patch literal 28002 zcmeHw2{=`2`}annLZXDq)FksZ&t;CxnKNznHnrJo+Z>Y2Npd8qq|#(eWGo_7Ds>`c z3duYr84};UROjqE^?Kj;|G&QL`>u+BPyud6}7mVGS>g`(Ho zuWE=wQ3xP^X{q6iU*JhE_-Bp#esfP0Y8^B3mjZR}JSz&N#^Y>cMl#dUmbE9i3SqDW zJDiZOt2>NFq2%}ax?}7Q<49a~I7eqU1)j0eavm;ctOAdzgpP=gy9&<9dA~mqchFzg z$lm|3y$qITui{#HUs+J#iX&mTd|mNwp0d6QJWG0I;TSnB%)_-bgmhSeM+vzhmzj<} zmkNQ1RJ*m@ryISX4$3jg}Rcl0{2$ef!~2Tnk6? zM683Xp{n|~+rcLV9w!pXT~=7w$Hzy=M@)!7bQDI*$jAtbhzg5}3c?6MPd_&j##hkI zbNg2%-}R{CJnf0j?j&b|8yBJ%V@L2JDe&+hH~Q=6>vLi4|GJO67ZJa77p%Q74v%xi zxsg1D(L!k9U&#SFe+jyIe#0El7xu-tgOJGYgmiTNwV12xFC{%mhPb~G`Q;5gjr`nk z!iG3cf)~*q2gA4jYTlkCHE)388?^t1A~kQo;Ww&u96bLWRXP|qX9t`oNt1+g{iP;n zJl@NbM8uE?#9!+p8}M6j-N{Vmhv$L|ASCunT-}`rB!Z_C;Wvx<*X7^dmE`R3n{`31 z{8kL->--xrVcEvd2gx%d7<%Rz~`L|&sBT3FAJdR8n zbS$}-5}x$q+3y$d&WPK|;xTTH3Ov4oSeygK3s2%v)F(Q-k>rID7s5OLDEjs6j~bU| z^z+T-!Bb>)AYx!KB#bJCgj0lAAR#CsB`7Lpgcgw%m6DZ^<`NCd2V1&E zz&bnl{aaf89{H0F6(SC1@m+_wAX*ecl98yGtQcArEkiyNvXQ^iK_D6t2zbS>(NF)o zA#tgwaPgWtyI~1Fo_oGN(UP8TqkmMSqa&;7=1Ic1+2b@-72yemoSm_<;`TCPVpyz{ zpqK;}BPb~%E-EO4{E`wAK|5e%WJDb#(My_t6#s{6Rf4@262-o$#)4`UX*D$!w6cVR zx-?o%9F0~&ODic$tD}{aBqY$HVoKlDl8OF9tsc=C5(x%RHpa^fbW5m;ib%_dipz*8 ztE(!b(K2XpC20v65mhm?gow0?^mpCAkpIU$3~_E)XC!{B;4z+_ita=L!9f-r`MZvv zuKz<`6-RXT#$nZo1lOeu;Eo}B;*bET!1Fyq{*^Mmx!6*Om398-a)^jvFc=3roS?Xf zgrp!^N?cM9gA=nCL`zFcNTMahMIA6$dEtME|2-$f&Awj7{x>V~57A#~Cj8fn@>TsW zmZb)$a9+J=!vJxWSbBDf~0`MDhhuW{C?tRkzc2Y|2LND-}PX`4F02~`duFTuUG4L zY2>TI|DP7kp5W$zk>$ZEdN4;>;K)y`yWTdf3J4=x`HdN#`QI7{+Qjqr^Dc&B3*#8d$e(eFs z5aM^4|0q#^^&q&Ptb;Qir;i~yDXPj^>JterID3+(rIH&&2fU}Hk24-C2<;{ELC^<- z$GcC&i1o6^5x+Mhz@y-u?T8qn zAD4`f3^Z0h3?di)-nPi95by+|4grf(6ocWvI87#nw6?OENF!?MjD?kv7J*y*eDas$ zwQ+8aARzgpz_+tMODH>&JoRzJgBVx$U-q*I9L>k+^9*`>$- z-Hhdzt}Z2a$Z$kwdrddUbM9cKn59A#3dLnc{EQ7WQBdQ3m4;?;Nt*oJ&*Q&>h`_1u z?2E%Ai@YTB^ZB3U%#d~o@3k~bDNzyl0U8f}zKN4x{#n%gUyG7o{#o>INnlHzxG zemYGig|H^FYVgYh$_J#h7ZDW&SpISPXOShVKL`XVPEk@qLJZ2|B@wdIKT9mZXQt{5 zjrG!v#1Jf&gnqvAOG)$JN|IgqCi&IS$jHp4Nc8nAP^G}q3jamZ{v$|zb%+1u-+w>S z|FU9obt^X^M`PLJ6}Xm#hWijb)El;93?U@&PMwk)yHf z@d{kaLPS1b1uk+lmOWm9YgvfM2duzFj>fXbD{w6f5&3`>xX961_IL%ZWg#LTumTr3 z8p|H9z_lzy3S8u9EPK2H*Rl|i4_JYV9F1j*t&T$NF40s~GV*;h+#XC?&?;pGl5Ne}8Rg)Fj*F@WK0^6{G{ za>f5j(~T>%+~>Xo6Saww6f$-wbDsG9PhU&SH|3t+nyNaU6(Q^Q0-bAy+2BQcL6-*p z+_R3$%M*Rq+j}K5bL*x}n{H%fg{e}~rbS%8zPs$+y{^8#2)Fi<2le&q4R3A~O{Nn~ zK0*Jug2!c%Z$B+Xta|9+pv%?k*L!9rdZdFFseEG$7 zJGsH|D5dhCFK@fLDBx>iOe}|)nA)vPv$C@ps;jG|gXX0fwn`u644R>sot+)| zG<_^IbmDF3db|0V30B1;((_-wq#5xQZwFum*Js?i#fQb=Fw>*$&39e52w9b2XZI!U zxA>x&TF|+MmbPLncroDVc^T%w&!5z6ZMl5bQpG%y%`%DkbaWCf zq3O@xU3I2wnmr=@W)$;oyO)>MipK7Levx%ZYLu0lCBAswW}&w+!uKoANgUQUFu-hX z75Dpq6|gR2-L8J(==JN42}gU1HdDo%*VfUYzd!%sK&JT}YRBj2ZW+!Sn(ZqNA?__8*@(bjN;cz(Lv3t``E1#I_n!IY7 z{ZJ=-rKspE%_^#x;g-3nA&rRo9_ikW$;t8v}6o5<=BID)+7%p>uQo-XYp$ zlh5T|IEmwFL0!VbhpO{4J#o4%oAT9p;<M`lI0bh8LqO2_^A zrzon-d^L=IUlY6K(UT{G`8o9;&wX_DYrAy+K8IdSy?0buh_Ih``?%E zbj?2b(O&_quVrT$)jK?;i;k_#oM36@?TrGln+wE`kYu;PysHmu1pC62hiCG5` z2;9D_yL+vvsj08u9s?VjIEXm?Bw1KlCvkQNG|E?AiU)iag*XOoEq=2N0;}Y(tcWjEoyEWsf?TO7HGln|J3<{Dlj#o_)`` z0a(D2S4c?R%IbpomdCyF1*~S+$=pGK5D8MhzWnFmJCSj51aF1(KdH3RY39F_UNX(u zf2TG&I{MVPbJ16??wFsStS=%BFcFDFePd%Bi!Ee;EsV<2^HX){hI<^IpO2dE2w2aT zc&jm|Q#_Z7l9F-DmYC*dGt@qHb?Tt`35~6aM_9jno-Uk`Bc%||m0go7Xn@Eh;?b@8 z$C)#-!-vm`;?^pD9u@<)GpE0NW>r&G$)l;MDR(a~oNdAM7KW6g7Ehi%qqnxUW@t%V zeRO=>y=?0BAqPyOWq;eP!)Zf5c$~ulFFyuE2+x0u9!vC){#i=19SoG$1lpog{VF6?~358 zd6A~F%^_fDQ2a(_rl^TkW*Mv5J5|UuXzyXOh-4vsD=P?U(I<>I?>-50eJnrmkU26s zI&a8T+{h5J)T8J3v3E}(GL@B;A`m!vH?Fnswaa<(K)L!lxv`}wndOCE0g04M-x^csEDioRISjXn(ij;OdKSn7bWc~CVgS~D1oplThH4uWp zJ?siw&!(irWM&GCf(LCtjLOv7uiCBj5PK}OWBtY6lTehj=i^&MG0KKF2wP{OSFr&nra#NM^F zw?XE8iO<+cB$QIobkJ7>y?1DAXyDqpGmKe-4aBr*TEiG=VRjEfY22sVeOep6t&7|# zAn>$d=mah=u0F#^$<56z?K{q;9>?Lzg1=UlOe?jsG50Fh#!6}fNw9^G%+zz z<34mFx^CR(&p-b>m6FnWm1zexive>eHGSpv)S&3~iZd0%{ubNP^YWPb>Ql>cMQ{sR z)Fpl1TKLx{=9|K#5NaLpfyi5?o#hP^^&>l3y?CaoBFbsb4h_>Qs` zfqZS6y4FeeSV(If_X1w#*z6renLuIF*WG>W>{-_N9?r#m%F2{>_Vy{Oi~`CFt?wUU zOA@lO>k-S1w5TCfSkY2IbbDA`je)zG6}oNn|1|8UQMtCrujO4sgOM)5)^RohWlJ<)v}Z%r#83veK9_UDf?0ZP7$p=x6+WGT5lPnmYu1KcZZC<1 zmEEt`*G>Y<*kx?2qC(|z_^`pjgP~BU_2_wdNq`UNo0>ws*);44k*h1Ku%oicCOB3x za4x^~)0dC0^loHlzduPtLDky6rm?XRQXlW0Jxc!mvPeleJdFP%De219s~e~2=}&3x z0N*(t7bkV>*fFRsZB$iNFZqmFk55d5l$Hi0lxnTx773d7{W#ph9K#}58NB$p(;+l_ zr|w1hD4370zdy0IB_Qm%pIO7}TQ@zLvNJR3AnNbe(WwSAISK}H_1ZP%jK;fz#fe&a zbetTaV8!;s&9@^@oKUs3wM~%w(9qP>6Bj(cU6RmoB0M}?vYZ70(}A?;MftjDUxcD4q)^&#yc`H7pGLe6`*B3aDw96j}cM)RJxPDiT$ ztdUnxK*~7n6iIqm8j;U@_0gO7bN#1W`lse3SHFe=_PD9dmtY9T_O|HU^M$Qg*-w9( z>mGwOwj1lXHv>7#W8{rQMwk7Y0++{_*XKGfCzi z2u+Rc^eEx&$jCFQq>$5(*9up8rtCcKrCNJC%?ClyJD6uXAkTLa1nXgoK`R-<6S)+MF7^ zAcLR_%|yn7Rt$~nm6i!K8f{iW+H*}OCHz2=P}joeF%4&D=Zw_phf&N(k3d!ToFC<~ z+y7q2%-H$!TPK>z7mRo8-n}KsxxnIf&=-G-ePAh(;2@9DRv$FDb9GTSNKF$c`tJ^D2d~>c6tR=@I?-LB4(YXgQdg(u?w&iLN=r-2xM$DJ?&{4+DaN}EZyF)S;9dA7f?*qz zN;uu^R+mAOz5FJbjPUHPQ)7^*Od1**#!(KoGNxVyUPC5bP#DR#a2^pc8E!AN@%8g# zOJY;z5)jb7nx5XGlUM-ua3@!y-9%&7!^2|(*pl+Dgo458%Bm{x-qq_Jv?v{*scwm}Ih99aGO>H6`dy>ya&wi}y8Zvo}pv161RPw51%&*X2acvd%RVr0~Yx4gOW zW#|=;fe*NzCS!pMs?cjGSy-IY(d|ZTx#k3$1NYEVx>wxNtq~!;8>3@lVoleddW`LF zzb-6fUFMYOCSWYAOndd})l1pg*(!VjQL2=1Cxw7d$n!jU^eFf7Gwl%rZ*R#y3^+>0 z0jDQwyEHUCqR?kE?|EKNPfsf=o4;EkE;zz8K$69`;b zp8;azs^K1Sc6zM?2cU6qE%V+~V5zUqxFsZ{_lQZhWyq`RdR^}xC-Cy}IuUD=4jCVQ z_GK>$`pfpgXB;<1{HF%12cMkusBhz5SfF5)cE2>SNa&L^YVd$Uw1t*VpzjXU3B#?0 zar9mLEUm1N@<4J`>G9{i&A08%ZriOc#WOfm9HoGGVXKl`N#yYI@-i#)MOQtc5Wg5F z>HpE`QSXuUjzM%frVP zO^@j_TfBP(I&*GeVYn}sHvi4Ni?j9PlapfJo1!+ETsHxe1r)~lXc-p0YD2fKkq86dT$S)!*-wX<5wF(b0jY zqdqgqDNs-pkp75Cw0G(1(_OZ>xJ0@zFpIM;ZF^e~fb zYJR?WTaibVuQDs}L9bX%P>{M@KYdZo(~*#Of4dK<+zbp1$|PB->SisAiPgiznsDYz zMMcf{d+*+1&&&Coq<2tRukexau3LLF%tw+MR$`zsY~71_Z)1a3hs{Rb6s*&epI8MZ z5i~cj8}db3qbXf_>(CtA4I@5zC~eI0oi;$V>~@y}&0aJhXb9_n=&&-eTI z61%&rV_vuv2$pp7wg>66y^$6d-v~BTdxA|&1el1Pl(nJJA-zU)G=WHd5tiMedlq=9 z@%FON>2>atPuAUWc%}@iK~2Ysl9G~=WT2`m?tDzawS7C4OF>C>JF{bE_9)4?WoFhU zXcqUdwsyOyXx`&tuK?%88e@(OMQW3XNA>jt(IU4ItIp)gTON`5;`ww#5{k)(24Csz zRXcKIesPrExi_}v+=mZD6lHJkrW+X<5tc#`jQiBo*r2pKk(7!i$=w6CaCDS`dljUD zOGbRqw^Ovu8#TY_JY3fH#y`Bg>d+l@?i)1=3w96za)OWcz$OpUV!i^t@N6vJqeHBc6UR#Uk^V;rKQ_> z_lIR$QKjkg!eqNfMpoO{aKefmKYsiYWLU8L(DL%&1Xo9Wn6!?S)ixx`hZm`xf@anp zI%ZZWx2@rHY{#Ja*e8^w<^}?ZS2lL=+Sh{T=t@MvMD6?bp&`DkvyG zN}El@;xgmlruuq4(#RWQ=yRdTGsBvi7cb#9}BcnIWt) zv$3(scLnj=-;8D8R1|=4*1A#PS=j0HS_ordNl8gC=Th1=Yc@c2QR8*hFzQ+5<8eZm zGIt4SfajX-1wN<(g&SPF?zlR#TU)q>BBaEr{X4q*QiEBbUV{n_uo&JI>H0+sQ@P5( z)U>w3ZaBX^O(o*ER=P`LNMCR7&Ofy3S+e-yQOnEAS2ex{ORY}S+R4Bn{|LFW73c*H)-h)N{Sc}#mW17U#kfacoJd@{4Q-(Z^b8J# z7zmb<3b$?i2hv%L4PL){|2|qNl$tG7k1sJAQx(33lQY%CXPw-n9&~sQpI?x>uyDTT z(q~s7KxhGz=wRpupIO(o1EfNC3n(tkFvm>Z-NCd*Sw#hHTY=I(C%z_5{sV1^ z_sE-J+qsX*+D%P}Zd#rcgBA_a^vQqiHLI4Y7 zgNHzKwBqChF1(H1lc-fxT>PM_3K%#h4h{}rzQQ19|Dm;$l9KY0;ms36VC*w9Gl*C2 zH6VFTiEtD=F8 z0wk0e8!KIT33VdF)D-;-Cl`VH40?Kc5I*f+TvE~vzOT8ey!o|IUS8g*`1lZD4LaX{ zXq%@E0Ir`^yx(oUYkhD9Qqe+e85)Y=Q1E{{S~^xU7BnpfVZBDWC3iyp+L`A*lt{mc zWC?`JeyMq+bbh|XcC168W~^VL4;)(BYjC6YSV!!=fa#9gBOj)wz=wq?si;8dI_RU2 z))xpkr1dQ?-)qFT2{3p&Fwk;`BWYvCi@0%dBJRX=xPDTNMG}f)!q!fX zX=q!0Kfcs}GJniBY!gQZvrS+@)nK{>5V$8f_nzFYang2i;mdwaO=n|c5Y5B5UCozzS6+fF|x8cz53F0F+^=^Ro<9SthfB5FvhczuIwFw(^h5UmFu1ax_a}!)vT$o?4WUGSIfGF;Fg5V1w%Ka-Q!C-NN00iQQksG$>2r*f083qx zaII&(0u9*JsO|32llr?8Q&WYh*K@QDo2v5g@IbCCf#l(q@qRUwW;)QvMS)06=iXonW65MCkP+lh352VQnF; z{yAI{t|wg^#~BA4Q(vi20ZS&>`Xkn_VszT*XgTj}6i-Pjh3|k|C zh-&wZovcYTFf?QW3Q@%G!yl4*cg43Q81impmGc((G}gJ)BSJhMhjt`Lg8;hm_^D8e|lcokYMNRhA7q~3^p}k85%>81(sEmL`{teCj z=C=lS>7I{-4Ft*}83)_|gVgyGz)|m};}5-YgLl>H)oGV5Rp5&wBIF?Iy91SRUS{$@ zMx|D|A?8iKvsp25Cm4Z4K}z#*^YIHECu4@!)#jc*0dCxRKQKjL>`ehb6B83cHiKCf z_7~Gp4Yn2%Y9XDr?Z`a0?)jNjf*JgFVZHAlDm_&c6o2&mc@4z0SPbT7CE5yN_em&# z-zJ(`qe~?=m3qMLgSUN=la+n=*rG%&qX{I`nsvfuM;AYrFWPTD$!bQT@hP%o*t|K3 zW%WV(g@py^9l6g4+b|FaggepENv7P?EbSDoTeb|s#1O{ZZ}Ppwz%Gk9sh1Zjlzbdx z_%&OP?S$f2(+qai`%h}=>D2;`7GDCudL<+z@cV#ktA)+1$^<>dsLaev7Gv{k2iFY_ z4RI%RZdH1Vl51HD&#xL3q7CPfP{B{aUlSRN-7lW!@sXjjVn0Zq|BE{G~{imop^@MeC`bF z&RH4HYv!lS-!wvmMlv|E>s~>%o=Z+{OMA)cuCUD)YsKp2?OE`20)R~^iAsm6cev#a zt0bOPoET`6-G)ZVVg_5_-o=g>(zHeJW!f`KAt_v_5<;=kLYf4s48xTpzUdF;qj{~Oq1&{Zdn<^#8nW9$h);(VV+n?v!6zfP& zwRqxfJIegbO1%XM7t~<86DcUPFUqrB*S(O}OfSBw75GSm1|*GjT!>X4W;*RU|79## zLrW{VTJHm$*y~c*7-t^;$Kd8m-VBJ1_w9MIv1o3hnN zqO?xnV^{^RE($fk04Pfx6=iPKgiW!g62=cWBVT%F)cQ_TeMtcr=Nw@Bi1 zSxv%i6CPuct&^%~n9mhMGo;L`g_$fRIne{)P$wyRNS}EsU`1p~8R0mMmn>4MV zr$@TCBS(%j+rpFLRWcgwV3V7r>WRLyX1ZafRq1QrEq<`e)uIZUnNX(MXJA&Nkmi}g zzn?qf)mC^Trh&V$5#p_@7kg;es3gB>_VJ`7(*&;VU|kW9A3uH&!5QQvk%_VeBd?J$ z&CEu2bJJc8Um6wiuy9Ue}%r?tTW! zRv;yy6v)cHVt8{L;-_+&e(nR%wXI%+Ivvyj39~CM#e4Ss)8x>FFQ4zcgll&(XV19L z&v?M=EQjdDp+bSMnOA~5j$(dhtQ8wy<{IW*w&QOf;K3K-U2%?%JrJEuN>ALsf8Tee zE5h6gUgW_OUR~3HcW4Uce4$hydxPwozjv{1um?y-U^?x+5M#UtbjsT|1&2`r zdX(FMz2*p-!ANdI=RW#`Pz@y|vM8N$hwX`42uIyPmk4VW0JV{{|3}`B{`XplD(Q`- zJ_MlrW9boX^so2^>x9cyy}Syl!Nj1$n5bcbz2{am{Z*(Pkxg=Qq2osj3#EVyv_~OaN&sgL;ak;U!3N}W8v-tWT_dk!yZQQ*1&=GB+r0u(Qg^RTU zRk$m6J>+$%E#;!UBO|eU4qnTRk3`Pi19AZ>9EoHB7pUT)ZKw(d>{BW}LF?=>HE5jC z{s3MLseJZq>47CfT+iy7@7L8;$6(T`r`p=vXVn~esLuo%o5@~y-fa)b1&K(_`{BK* z)>7XI9@vl*QzI^!-;bwsszw_itZ7mJF`@{_% z51M!FIM&osU~a3qg9*y_?#cRc*h~Hl+mcIt1hR3|6Sw!dI=orday>d)>s?8I(HVKI0#YS4ebg!ZrA6krYPPcNvHt`9;S4VT literal 0 HcmV?d00001 diff --git a/core/broadcaster/src/main/resources/mocks/index.html b/core/broadcaster/src/main/resources/mocks/index.html new file mode 100644 index 0000000..75091dd --- /dev/null +++ b/core/broadcaster/src/main/resources/mocks/index.html @@ -0,0 +1,81 @@ + + + + + SSE example + + +

+ Events: +

    + + +

    + + + + \ No newline at end of file diff --git a/core/broadcaster/src/main/resources/mocks/react-3.png b/core/broadcaster/src/main/resources/mocks/react-3.png new file mode 100644 index 0000000000000000000000000000000000000000..dad56a7a6f902bf10c87cd4e19a2d7859994b0dc GIT binary patch literal 5425 zcmV-170&93P)EGbr^=aQo@0ishN9M@hRKG0#(s*+ik#D&pw*y&$AFN|kd@Gtjm?dp)SsEs znfd(rh{}kQ&y$PHi+#m?gvf-Z*ruh|rGm(Urq`y5%9UlHWu4QVcDr_Z!g`w0nxxmH zaHVjT(U$o7_?OX_q}HT;#(nDb>X6Nlqtv65&8C0GfsxFin$enxvx(#J<7lO5ZLMv^ zePj-g^RR{p1_`=zM+rKkDbYqp~;n`%9o(fpn}JSeZ_pd(!A{P?54_@!PLQ~ z%9v%IWrePVj<}A4$B2i|f`mvYo1j=h3ThQx z3X0V#R6*3bwJx>QF7Lhn{Y_>vLnfJI?wz^InPkq7tzvRN-19x>+;h+M`CMU@5mr|5 zd1h^kH<%zgm_$pd0;TbzJwaR-uw31op2kOl5QsvZzS1EUAT+(vkr2gDsaq*$v3gOOxX zl2WOedOVYf#S)2ZIGj$0Litiu6g?npHKZt5T9E*<7+4_CqAY=g^+N_72(&z;o=Qnl zH5my8Yqf$v!E_*!m#O1t>a$|BRLY0a>2NrkNW@~9OuXJgmb8gX2QuZ=vS`$@a-!Ho zwjm=6ClW_8v#)K)8nv?9fDC@Mw}njEW->D9K!)@@1TGMmCx)RLEB=+$5WTOR0Sg4= znfkeLf78A>H-tbScAbP&9)AD$9z`H2m_Q)*Wv%#iI|vOO5O|%7tNu2l877w*?powC;CZK&ZN{I9wpm?uV@SyNrU{f(!)K4XRbYiII0(GLRXlKtSCwocEjC z0eK*Cs6Zgy!tE9i-EGYc4G>88)K~mHZT#BM0D&{fVb$NyZVU?$FtbkP{r!!g+nNCl z1jsa?-U342ZKXg10WvRZ#cyplh5`tTiE^uc6eHxeWFQ&9Kp@OqocE&{8Mg%-2!N@i z)&fG*Z2<=YU~cb+7av#c%xh=FDxu2jY9dLf3DaR^Y1$B_Ghgd@sDiAb1ndPr74H2dkCO&|#%IMPW4M=)y^`_jC=^N6s3 z=t2^5GmyNb;AqU{O#k=4^bQY~{v|FTExYSpAf=RgNj28<>hK~hB1?-D1f&)Hx)n$? zuH2C<#H0O+T6CI%wic-fh;1NA{y@ZxdMAG-+W*h((jc;B8t@Vj5GByEu^JKHK*U&0 z!7-J$F8nSr@>fIzM7BNm0m&tl#6zee1tg24Q}%Up9yk2lJXRA(mV4rn3?u&ef5=9^ zkN)w|6A*h*FIOO~#3O?70f?TlSIirE2?>bbUO9^&kdT^q1QR)|zqU=PuC>tXt`s2w zd2?pgO+fONiAT?1i;R%0_qzT}NI-6no}aX{FhMRrqID(lC}-?U{mPLzAE~@FCZ$nywy{@5$nzGg) zBp}Nw59k^as@h6C`Z}&1obuMvPM@GIToA7PL{dQPZII^hQ#L^IQwok+jPN7!FDht7 z;H`?qOVEkJ3Q+;Uje7G;fkbE2JTN1Y|JiHrD^~ktsq)_Xt*^Ue1!NQ}FU-XXI*<(* z2T;!BWV^C%E+`A@m2ITgJI@gokOx@XBdHC+WB^1vfJ&9<{yS^C+dGv<>q}c(3nL3# zTTAPYDm&Y|sITCW%|I{(BvBTa0Z}^Nnu$l=2igw!2}o2avj*7!Q9Iv=Bpy_4kDq{K z6$g@PscV5Mklvkd!0a5i3?!~ZoICSYW+7Y^2n@J@e z{X5?VQ9$nD3W%s=1I6~P23g*zsIAudMkDcnDj*ki0YN+~Oe+FgEz$pm^b-$&0&*5p zK*YFmNEf5;K~Ov2hFd^FO2Ct(pABGiz74T}9(bK^Ln|P8B>;`kUnJbl zw;>gfn9@`brylUw=zJSS0Vyd5LqY1r3!9y9eC%Y{ZqWf!0s`Jg6#Uc{^EKgDbq6xZ%fQWU)tIyG034)z(PE4+vMtBLx9W(*i zU{Na3I^VwVc0N`7x6Mci$P*+1nawCFOSh6pq4UkI1@XrZGZm10gnp@nR_B|oxI)0b zyUJETl$<$km86uiaVgR$2!)0$iCMg3l2+$i8O8xrUj8U}Dn>{^#{c*C^aO%{l!^$Y zl3>Sc#lbl8j)Hao^+oRqKMAWj#~0*Elj zna5q^iP7j$?>tojX<332kU}1ofN0kBh&g)H8ycr8AgYy1bbw$bzbphq|NY$Sj(Wo| z1jNz8SppHl&;&%YgOIJLmw|vdSvYGT5pV)x>b6j$UM2#fmbEAW;kQLBv$uA)RoCwn zvlS513x@!t3Qs_EyMY_^5)zO%EgruSmtO=RLJ6RN=x%EcE9xaAAh$IExz%^!qLhHd zAPR`?wyx<>?;I-udDs$=U$2l~I20gB9z4+3?gnzyOG-eVA_$1xg+l=%aHuijtlc*u zHR{bn5)j2iN{m*uJ35dOgL;Jzd1;nx?h0tVxzgj#l3j(c$L;A60_yW@3Z4r8(<#CJ0@-0Lj z#Q_j?r{Zhowg|n?@=7}cd5*e4Xo)(*u-Ez8jdwj9Na%f*aZCZB81}MD42i7WK?sI` z&`S*2l}^a4ov|f_mw-?#oiHmd5?VWh+u|l56pM>d*9gd|+u|=E^vdr^Hwj4R*)Bf; zp?5$PbCrM?&vrQq2)!fRlDh4qobddwrVT^q1H;uXoJAz(Ht!^-I@$QeS?6xjxZ0F zhdP_=&LO6N(7KLL&JQ~fWj5Kni7`w8p><&=;bUaua0Nsm@*g|_p>?H=-KEb}xB{Zi zCY!?ILFv*b%WKgGa0Nt}O(sS#cu=|)-Fg2#8p%+Ul)F#`q&3_!8bsznX>dT-gowE& z`wXoiju)060u_+faLYU@5lW*qx@V|VTmRly8cZhTX#X};0nt1Jn!lnme8yzTP*y77 zk7h))Gs+mrUw{gT=CdV~1cb_XrXF*+#OzdAXObp4G3#6PyI(#FQ$RF}6|8#W=nOFH zGYp#!lQXR^56Er(q6vuMgQ^cl^)6sJje2Yi!4$J8jY*?UhRNipZb01D?OyNYBzkK* zogtVJl;M41I91d-if6*JB!R$iTjT%j`Q&phYcB2)5>pQ&_6I4?hG#S;MdwT=+K51K zTTiF^jn@ywJGV)MRG6cV8ZQfF8>7aXOllgFRM=5k|6sJ;xVx=4rUF82;V4=zU2o`2 zzBpiV!9aww+q#YW5Duwkm;TR;vOcNQ!P)~WHLKBVL)=7 zACSwa4}5{P4V_6rvd84iw+13l&VtH7+-RXTCJ8$g1f#Uyx#$Uqb0(RH6D9!Mmiq;y z)ABkKp^D9fzuVFTjNgR znVdOq*d$lax<^1{i_1}e$>)R3L_d&6Y+Nx`8<#J-ML-l!UBG_^mMasaV;AObYnfaI zl9G#hQ6GF0+Z$IlfVnM_8AzVXWJH@x(3>P?S-P#qL`zt5q8|xge(@?arB4Hm=BSYvS{m zZf_{GRqFym?gi5X*3+YqtgUgSSChnrHXgFudI()W6qnq9TVh0D(%vM|{lBFv?LdgT zt*cbdK$<1Skk99SCRMZ2(DR=SNZ!B}5O3W!@`4)w6>+-LZ+!hT1aZ8EO z+CpEuF6c$oNUVJ22P$rhih#JN#E5S*TIrsS+qz2c5Zx^$M*Jd#megM!Bks2DQS8EU zQHc@5s_k9>9nseB!~|r5w18+O#+o=_JT{E?Fgo1Ub+7{B$`T`rb&j;SWprCtomCs3 zP;RSsUy0E&nD4&!`W-p9Wt}y9Rm-(JA6LMRplL2NaeSEi;7!f6%u-odM z3_#x8TVm`{q3dx`pNQLf!r)$xt4oailMv!=i@JaeLW!}{Vm&YEJ9b<21!Pc4jHYE1 zYq#|Zo`BdWG1fDgSRxS)r$hOY2r#Ft_8TnH;kNF96A&9E#?Du)8o@|Xno8Beg@VKo0MZLlY4E5+mY!d^MR~E-|vZ2&a;wXV3>NedLn8^V9A2*K!1|wTYlR&wz(JiT|o9X-v$D% z1tOv+NoAwkdU_G0fb4I+4g_2b1X0yZ+}3CFTcUgghAMDYqc-yLc|Cd_V$autvd$AN(Rf%x!Vw$a?y;T^Nf?OX-K%3HYwNC-2r zY`LxT?@TVYFY}vCwvVtq+yMkj*0pHs@Q&H#c8;^jKE4eM43G`%+L-LNv<=d9TaOvE zRa>a}W*{&)K>D7m_i$Tx3=<&KlDrmRu@{isabTc;M6nCWHPJYeWAyU(ygDSN!LDa_!uSPRHzV1R+h(XVAbQVVzgtv0uHiSY;i7#M6I zzG$jHkCbT#tv4{Zgs_!kO z$p@3nD~g+ep#ajHB`P##Rad^erv%nAm zae;jbct9Z8A159VP_`NHfIzbS*B+38*dQJdU>n2(0&IhLK6Z#^J`kb`(YXxTFl z2&^hrzymTMlI{VaVJ}ZSAOqvEJRrb4mInk@jO78LQ8o#9KtS0?56A#XhzEpx(0l9w zfd%BP2V{UW-2(zl(>)->?UM&&kb*1^2yr|1fOtSWqgBcBfRMKn4~PfEvp)9P0|LvO b@d*4M5LXcYr#0A500000NkvXXu0mjfp7%DG literal 0 HcmV?d00001 diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java new file mode 100644 index 0000000..29116c6 --- /dev/null +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -0,0 +1,87 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.document.JsonDocument; +import com.couchbase.client.java.document.json.JsonObject; +import io.reactivity.core.broadcaster.config.CouchbaseConfig; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.BiConsumer; + +/** + *

    + * Some tests for the Reactivity repository. + *

    + * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = CouchbaseConfig.class) +public class RepositoryTest { + + /** + * The bucket. + */ + @Autowired + private Bucket bucket; + + /** + * Inserts 1000 random artifacts to the repository. + */ + @Test + public void insertSomeArtifactsDocuments() { + Mono.fromSupplier(() -> { + final Map categories = new HashMap<>(); + final BiConsumer populator = (key, values) -> { + final String value = values[ThreadLocalRandom.current().nextInt(0, values.length)]; + + if (value != null) { + categories.put(key, value); + } + }; + + populator.accept("assignee", new String[]{"ndamie", "gdrouet", "asanchez", "qlevaslot", "cazelart", "fclety", "hazarian"}); + populator.accept("category", new String[]{"bug", "feature", "question"}); + populator.accept("priority", new String[]{"high", "low", "medium"}); + + final String id = UUID.randomUUID().toString(); + categories.put("description", "Description of Artifact " + id); + + final JsonObject object = JsonObject.create() + .put("version", "0.1.0-SNAPSHOT") + .put("organization", "Organization/1") + .put("updated", System.currentTimeMillis()) + .put("categories", JsonObject.from(categories)); + + return bucket.insert(JsonDocument.create(id, object)); + }).repeat(1000).subscribe(); + } +} diff --git a/core/java-lib/pom.xml b/core/java-lib/pom.xml index d118623..fe9418c 100644 --- a/core/java-lib/pom.xml +++ b/core/java-lib/pom.xml @@ -16,6 +16,7 @@ UTF-8 1.8 2.8.5 + 3.0.1 @@ -32,7 +33,24 @@ + + + src/main/resources + true + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + @ + + false + + org.apache.maven.plugins maven-compiler-plugin diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java new file mode 100644 index 0000000..daac0bd --- /dev/null +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java @@ -0,0 +1,105 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.lib.event; + +import io.reactivity.core.lib.ReactivityEntity; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.UUID; + +/** + *

    + * An error representation. + *

    + * + * @author Guillaume DROUET + * @since 0.1.0 + */ +public class Error extends ReactivityEntity { + + /** + * Application version. + */ + private static String version; + + /** + * Error message. + */ + private final String message; + + // Loads lazily the properties + static { + final Properties properties; + + try (final InputStream is = Error.class.getResourceAsStream("/io/reactivity/core/lib/application.properties")) { + properties = new Properties(); + properties.load(is); + version = properties.getProperty("application.version"); + } catch (IOException ioe) { + throw new IllegalStateException(ioe); + } + } + + /** + *

    + * Builds a new instance + *

    + * + * @param message the error message + */ + public Error(final String message) { + super(version, UUID.randomUUID().toString(), System.currentTimeMillis()); + this.message = message; + } + + /** + *

    + * Creates a timeout error. + *

    + * + * @return the error + */ + public static Event timeout() { + return EventType.ERROR.newEvent(new Error("The service has timed out.")); + } + + /** + *

    + * Creates a exception error. + *

    + * + * @return the error + */ + public static Event exception() { + return EventType.ERROR.newEvent(new Error("An error has occurred. See logs for more details.")); + } + + /** + *

    + * Gets the error message. + *

    + * + * @return the message + */ + public String getMessage() { + return message; + } +} diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java index bed0829..d38c82d 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java @@ -43,7 +43,12 @@ public enum EventType { /** * Read organization event. */ - READ_ORGANIZATION; + READ_ORGANIZATION, + + /** + * Error event. + */ + ERROR; /** *

    diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java index b1f6b36..b815e65 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/package-info.java @@ -17,11 +17,6 @@ /** - *

    * This package defines several events that can be generated inside Reactivity platform. - *

    - * - * @author Guillaume DROUET - * @since 0.1.0 */ package io.reactivity.core.lib.event; \ No newline at end of file diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java b/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java index 883a0e9..13bedf2 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/package-info.java @@ -17,11 +17,6 @@ /** - *

    * Base package for Java library defining common fundamentals objects. - *

    - * - * @author Guillaume DROUET - * @since 0.1.0 */ package io.reactivity.core.lib; \ No newline at end of file diff --git a/core/java-lib/src/main/resources/io/reactivity/core/lib/application.properties b/core/java-lib/src/main/resources/io/reactivity/core/lib/application.properties new file mode 100644 index 0000000..6f3f5fb --- /dev/null +++ b/core/java-lib/src/main/resources/io/reactivity/core/lib/application.properties @@ -0,0 +1 @@ +application.version=@project.version@ From e0d8076695197cb0f11718d54783211b0978b570 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 19 Nov 2016 14:57:43 +0100 Subject: [PATCH 004/132] reactive-stream: better error handling Repository methods now always return a ReactivityEntity because it can be an Error instead of the targeted type in case of success. This allows to notify the subscriber that an exception occurred instead of raise a timeout and also to not swallow the exception. --- .../repository/ReactivityRepository.java | 11 +++-- .../couchbase/ArtifactViewQuery.java | 4 +- .../CouchbaseReactvityRepository.java | 15 ++++--- .../couchbase/ListArtifactViewQuery.java | 19 ++++++-- .../MockCouchbaseReactivityRepository.java | 11 ++--- .../broadcaster/service/EventService.java | 45 ++++++++++++++----- .../broadcaster/service/FluxDecorator.java | 7 ++- .../core/broadcaster/web/EventController.java | 6 +-- .../io/reactivity/core/lib/event/Error.java | 17 +++++-- .../reactivity/core/lib/event/EventType.java | 3 +- 10 files changed, 94 insertions(+), 44 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java index 2dc7eff..c9957e8 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/ReactivityRepository.java @@ -18,8 +18,7 @@ package io.reactivity.core.broadcaster.repository; -import io.reactivity.core.lib.event.Artifact; -import io.reactivity.core.lib.event.Organization; +import io.reactivity.core.lib.ReactivityEntity; import io.reactivity.core.lib.event.ArtifactView; import org.reactivestreams.Publisher; @@ -48,7 +47,7 @@ public interface ReactivityRepository { * @param the desired returned type * @return a publisher where source is the retrieved organizations */ - Publisher findOrganizationsWithMember(String memberId, Function mapper); + Publisher findOrganizationsWithMember(String memberId, Function mapper); /** *

    @@ -60,7 +59,7 @@ public interface ReactivityRepository { * @param the desired returned type * @return a publisher where source is the retrieved views */ - Publisher findViewsFromOrganization(String organizationId, Function mapper); + Publisher findViewsFromOrganization(String organizationId, Function mapper); /** *

    @@ -72,7 +71,7 @@ public interface ReactivityRepository { * @param the desired returned type * @return a publisher where source is the retrieved artifacts */ - Publisher findArtifactFromView(ArtifactView view, Function mapper); + Publisher findArtifactFromView(ArtifactView view, Function mapper); /** *

    @@ -84,5 +83,5 @@ public interface ReactivityRepository { * @param the desired returned type * @return a publisher where source is the retrieved view */ - Publisher findViewById(String id, Function mapper); + Publisher findViewById(String id, Function mapper); } diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java index 58bb8f1..bad6c8d 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ArtifactViewQuery.java @@ -19,7 +19,7 @@ package io.reactivity.core.broadcaster.repository.couchbase; import com.couchbase.client.java.AsyncBucket; -import io.reactivity.core.lib.event.Artifact; +import io.reactivity.core.lib.ReactivityEntity; import rx.Observable; /** @@ -41,5 +41,5 @@ interface ArtifactViewQuery { * @param bucket the bucket to be used * @return the result artifacts */ - Observable query(AsyncBucket bucket); + Observable query(AsyncBucket bucket); } diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java index a45bcb0..6ec7675 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/CouchbaseReactvityRepository.java @@ -22,9 +22,12 @@ import com.couchbase.client.java.Bucket; import com.couchbase.client.java.query.AsyncN1qlQueryResult; import io.reactivity.core.broadcaster.repository.ReactivityRepository; +import io.reactivity.core.lib.ReactivityEntity; import io.reactivity.core.lib.event.Artifact; import io.reactivity.core.lib.event.ArtifactView; +import io.reactivity.core.lib.event.Error; import org.reactivestreams.Publisher; +import org.slf4j.Logger; import rx.Observable; import rx.RxReactiveStreams; @@ -60,7 +63,7 @@ protected CouchbaseReactvityRepository(final Bucket bucket) throws IOException { * {@inheritDoc} */ @Override - public Publisher findArtifactFromView(final ArtifactView view, final Function mapper) { + public Publisher findArtifactFromView(final ArtifactView view, final Function mapper) { final ArtifactViewQuery artifactViewQuery = ArtifactViewQueryFactory.INSTANCE.create(view); return RxReactiveStreams.toPublisher(artifactViewQuery.query(bucket).map(mapper::apply)); @@ -72,12 +75,14 @@ public Publisher findArtifactFromView(final ArtifactView view, final Func *

    * * @param asyncQueryResult the result + * @param log the error logger * @return the observable error */ - static Observable error(final AsyncN1qlQueryResult asyncQueryResult) { + static Observable error(final AsyncN1qlQueryResult asyncQueryResult, final Logger log) { return asyncQueryResult.errors().flatMap( - jsonErrors -> Observable.error( - new IllegalStateException( - jsonErrors.getInt("code") + ": " + jsonErrors.getString("msg")))); + err -> { + log.error("N1QL query failed. Code: {}. Message: {}.", err.getInt("code"), err.getString("msg")); + return Observable.fromCallable(Error::exception); + }); } } diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java index 346be57..e8655b4 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java @@ -24,9 +24,12 @@ import com.couchbase.client.java.query.dsl.Expression; import com.couchbase.client.java.query.dsl.Sort; import com.couchbase.client.java.query.dsl.path.LimitPath; +import io.reactivity.core.lib.ReactivityEntity; import io.reactivity.core.lib.event.Artifact; import io.reactivity.core.lib.event.ArtifactView; import io.reactivity.core.lib.event.Period; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import rx.Observable; import java.util.Collections; @@ -52,6 +55,11 @@ class ListArtifactViewQuery implements ArtifactViewQuery { */ private final ArtifactView view; + /** + * The logger. + */ + private final Logger log = LoggerFactory.getLogger(getClass()); + /** *

    * Builds a new instance. @@ -67,7 +75,7 @@ class ListArtifactViewQuery implements ArtifactViewQuery { * {@inheritDoc} */ @Override - public Observable query(final AsyncBucket bucket) { + public Observable query(final AsyncBucket bucket) { // Prepare the statement and parameters final JsonObject params = JsonObject.create().put("organization", view.getOrganization()); @@ -84,16 +92,19 @@ public Observable query(final AsyncBucket bucket) { expression = expression.and(Expression.x(timestampField).lte(period.getTo())); } - final LimitPath limitPath = Select.select(FIELDS).from("artifact").where(expression).orderBy(Sort.desc(timestampField)); - final Statement statement = period.getLimit() != null ? limitPath.limit(period.getLimit()) : limitPath; + final LimitPath limitPath = Select.select(FIELDS) + .from(bucket.name()) + .where(expression) + .orderBy(Sort.desc(timestampField)); // Build the query + final Statement statement = period.getLimit() != null ? limitPath.limit(period.getLimit()) : limitPath; final ParameterizedN1qlQuery query = N1qlQuery.parameterized(statement, params); // Execute and map to an Observable of Artifact return bucket.query(query).flatMap(result -> result.parseSuccess() ? // Map each internal JSON object to an Artifact and handles statement failure - result.rows().map(this::toArtifact) : CouchbaseReactvityRepository.error(result)); + result.rows().map(this::toArtifact) : CouchbaseReactvityRepository.error(result, log)); } /** diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java index 3474680..c46c426 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java @@ -19,6 +19,7 @@ package io.reactivity.core.broadcaster.repository.couchbase; import com.couchbase.client.java.Bucket; +import io.reactivity.core.lib.ReactivityEntity; import io.reactivity.core.lib.ViewType; import io.reactivity.core.lib.event.ArtifactView; import io.reactivity.core.lib.event.Member; @@ -35,8 +36,8 @@ import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import java.util.Base64; +import java.util.Collections; import java.util.UUID; import java.util.function.Function; @@ -68,7 +69,7 @@ public MockCouchbaseReactivityRepository(final Bucket bucket) throws IOException * {@inheritDoc} */ @Override - public Publisher findViewById(final String id, final Function mapper) { + public Publisher findViewById(final String id, final Function mapper) { return findViewsFromOrganization("Organization/1", mapper); } @@ -76,7 +77,7 @@ public Publisher findViewById(final String id, final Function Publisher findViewsFromOrganization(final String organizationId, final Function mapper) { + public Publisher findViewsFromOrganization(final String organizationId, final Function mapper) { return RxReactiveStreams.toPublisher( Observable.fromCallable(() -> new ArtifactView( "0.1.0-SNAPSHOT", @@ -93,14 +94,14 @@ public Publisher findViewsFromOrganization(final String organizationId, f * {@inheritDoc} */ @Override - public Publisher findOrganizationsWithMember(final String memberId, final Function mapper) { + public Publisher findOrganizationsWithMember(final String memberId, final Function mapper) { return RxReactiveStreams.toPublisher(Observable.fromCallable(() -> new Organization( "0.1.0-SNAPSHOT", "Organization/1", nexMockTimestamp(), "Reactivity", loadMockedImage("react-3.png"), - Arrays.asList(new Member("User/1", "ADMIN")))).map((view) -> mapper.apply(view))); + Collections.singletonList(new Member("User/1", "ADMIN")))).map(mapper::apply)); } private String nextMockedId() { diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java index 698f402..bd4b8dd 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/EventService.java @@ -21,6 +21,7 @@ import io.reactivity.core.broadcaster.repository.ReactivityRepository; import io.reactivity.core.lib.ReactivityEntity; import io.reactivity.core.lib.event.ArtifactView; +import io.reactivity.core.lib.event.Error; import io.reactivity.core.lib.event.Event; import io.reactivity.core.lib.event.EventType; import io.reactivity.core.lib.event.Organization; @@ -29,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; +import rsc.publisher.PublisherJust; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -67,12 +69,35 @@ public class EventService { * @param viewId the view ID * @return the event flux */ - public Flux> loadArtifacts(final String viewId, final int limit, final long maxAge) { - final Function periodAdapter = - v -> new ArtifactView(v, new Period(v.getPeriod().getFrom(), maxAge, limit, v.getPeriod().getCategory())); + public Flux> loadArtifacts(final String viewId, final int limit, final long maxAge) { + // When the view is retrieve, update the period state to select artifacts according to parameters + final Function periodAdapter = + v -> { + if (v instanceof ArtifactView) { + final ArtifactView cast = ArtifactView.class.cast(v); + final Period p = cast.getPeriod(); + + return new ArtifactView(cast, new Period(p.getFrom(), maxAge, limit, p.getCategory())); + } + + return v; + }; + + // Load the view and substitute the item to the corresponding artifacts return Flux.from(repository.findViewById(viewId, periodAdapter)) - .concatMap(v -> repository.findArtifactFromView(v, EventType.READ_ARTIFACT::newEvent)); + .concatMap(v -> { + if (v instanceof ArtifactView) { + final ArtifactView cast = ArtifactView.class.cast(v); + return repository.findArtifactFromView(cast, EventType.READ_ARTIFACT::newEvent); + } else if (v instanceof Error) { + return PublisherJust.just(EventType.ERROR.newEvent(v)); + } + + // Handle artifact view or error only + throw new IllegalArgumentException( + String.format("Unsupported ReactivityEntity in this mapper: %s", v.getClass().getName())); + }); } /** @@ -83,7 +108,7 @@ public Flux> loadArtifacts(final String viewId * @param userId the user ID * @return the organization event flux */ - public Flux> loadOrganizations(final String userId) { + public Flux> loadOrganizations(final String userId) { // Retrieve the organizations: member ID can be an arbitrary value as it is currently mocked return Flux.from(repository.findOrganizationsWithMember(userId, EventType.READ_ORGANIZATION::newEvent)); } @@ -97,7 +122,7 @@ public Flux> loadOrganizations(final String us * @param organizationId the organization ID * @return the event flux */ - public Flux> subscribe(final String organizationId) { + public Flux> subscribe(final String organizationId) { return Flux.create(emitter -> { // Complete the emitter once all flux have been completed final AtomicInteger fluxCount = new AtomicInteger(1); @@ -108,7 +133,7 @@ public Flux> subscribe(final String organizati } }; - final Consumer> emit = e -> { + final Consumer> emit = e -> { fluxCount.incrementAndGet(); emitter.next(e); }; @@ -116,9 +141,9 @@ public Flux> subscribe(final String organizati // When an organization is received... Flux.from(repository.findOrganizationsWithMember(organizationId, o -> o)).doOnError(onError).subscribe(o -> { // ... retrieve the associated views - final Publisher> viewsPub = + final Publisher> viewsPub = repository.findViewsFromOrganization(o.getId(), EventType.READ_VIEW::newEvent); - final Flux> views = fluxDecorator.decorate( + final Flux> views = fluxDecorator.decorate( Flux.from(viewsPub) .doOnError(onError) .doOnComplete(finishFlux), "findViewsFromOrganization(" + o.getId() + ")"); @@ -130,7 +155,7 @@ public Flux> subscribe(final String organizati if (v.getData() instanceof ArtifactView) { final ArtifactView view = ArtifactView.class.cast(v.getData()); - final Publisher> artifactsPub = + final Publisher> artifactsPub = repository.findArtifactFromView(view, EventType.READ_ARTIFACT::newEvent); // ... and retrieve the associated artifacts diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java index e1b357c..e140196 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java @@ -77,10 +77,9 @@ public Flux> decorate(final ProceedingJoinPoin * @param log the log name * @return the decorated {@code Flux} */ - Flux> decorate(final Flux> flux, - final String log) { - return flux.timeout(TIMEOUT, PublisherJust.fromCallable(Error::timeout)) - .switchOnError(PublisherJust.fromCallable(Error::exception)) + Flux> decorate(final Flux> flux, final String log) { + return flux.timeout(TIMEOUT, PublisherJust.fromCallable(Error::timeoutEvent)) + .switchOnError(PublisherJust.fromCallable(Error::exceptionEvent)) .log(log); } } diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java index 99c656c..01f33e0 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java @@ -57,7 +57,7 @@ public class EventController { * @return the event flux */ @GetMapping("/load/artifacts/{viewId}/limit/{limit}/maxage/{maxAge}") - Flux> loadArtifacts( + Flux> loadArtifacts( @PathVariable(name = "viewId") final String viewId, @PathVariable(name = "limit") final int limit, @PathVariable(name = "maxAge") final long maxAge) { @@ -73,7 +73,7 @@ Flux> loadArtifacts( * @return the organization event flux */ @GetMapping("/load/organizations") - public Flux> loadOrganizations( + public Flux> loadOrganizations( @CookieValue(value = "SESSION", required = false) final String sessionId) { // Retrieve the organizations: member ID can be an arbitrary value (here the session ID) as it is currently mocked return eventService.loadOrganizations(sessionId); @@ -88,7 +88,7 @@ public Flux> loadOrganizations( * @return the event flux */ @GetMapping("/subscribe/{organizationId}") - Flux> subscribe(@PathVariable(name = "organizationId") final String organizationId) { + Flux> subscribe(@PathVariable(name = "organizationId") final String organizationId) { return eventService.subscribe(organizationId); } } diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java index daac0bd..da4adee 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Error.java @@ -77,10 +77,21 @@ public Error(final String message) { * * @return the error */ - public static Event timeout() { + public static Event timeoutEvent() { return EventType.ERROR.newEvent(new Error("The service has timed out.")); } + /** + *

    + * Creates an exception event error. + *

    + * + * @return the error + */ + public static Event exceptionEvent() { + return EventType.ERROR.newEvent(exception()); + } + /** *

    * Creates a exception error. @@ -88,8 +99,8 @@ public static Event timeout() { * * @return the error */ - public static Event exception() { - return EventType.ERROR.newEvent(new Error("An error has occurred. See logs for more details.")); + public static ReactivityEntity exception() { + return new Error("An error has occurred. See logs for more details."); } /** diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java index d38c82d..bd69da5 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/EventType.java @@ -70,10 +70,9 @@ public static Event newEvent(final String eventT *

    * * @param data the data payload - * @param the payload type that must be a {@link ReactivityEntity} * @return the event */ - public Event newEvent(final T data) { + public Event newEvent(final ReactivityEntity data) { return newEvent(name(), data); } } From 6e67ab342c8f1ee635f0d53a58ce443a2e071f7f Mon Sep 17 00:00:00 2001 From: Guillaume DROUET Date: Tue, 22 Nov 2016 15:47:10 +0100 Subject: [PATCH 005/132] ci: manage the repository with travis CI --- .travis.yml | 10 +++ config/settings.xml | 9 +++ core/broadcaster/pom.xml | 35 ++++++--- .../core/broadcaster/RepositoryTest.java | 3 +- .../core/broadcaster/TestConfig.java | 48 +++++++++++++ core/java-lib/pom.xml | 20 +++--- core/pom.xml | 72 +++++++++++++++++++ 7 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 .travis.yml create mode 100644 config/settings.xml create mode 100644 core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java create mode 100644 core/pom.xml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4c54812 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: java +jdk: + - oraclejdk8 +env: + - MODULE=core + - MODULE=core/java-lib + - MODULE=core/broadcaster +before_install: + - cp config/settings.xml ~/.m2/settings.xml +script: cd $MODULE && mvn clean deploy \ No newline at end of file diff --git a/config/settings.xml b/config/settings.xml new file mode 100644 index 0000000..7ea8227 --- /dev/null +++ b/config/settings.xml @@ -0,0 +1,9 @@ + + + + github + ${env.CI_DEPLOY_USERNAME} + ${env.CI_DEPLOY_PASSWORD} + + + \ No newline at end of file diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 557b08a..4393d6b 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -3,25 +3,20 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - io.reactivity.core broadcaster - 0.1.0-SNAPSHOT jar Reactivity core broadcaster Microservice holding client SSE connections and broadcasting events - org.springframework.boot - spring-boot-starter-parent - 2.0.0.BUILD-SNAPSHOT - + io.reactivity.core + reactivity-parent + 0.1.0-SNAPSHOT + ../pom.xml - UTF-8 - UTF-8 - 1.8 2.0.0.DATACMNS-836-SNAPSHOT 2.3.4 1.1.9 @@ -32,6 +27,19 @@ 0.6.0.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-dependencies + 2.0.0.BUILD-SNAPSHOT + pom + import + + + + @@ -136,6 +144,15 @@ false + + + internal.repo + GitHub Repository + https://raw.githubusercontent.com/reactivity-io/maven-repository/mvn-repo + + true + + diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java index 29116c6..6809d32 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -21,7 +21,6 @@ import com.couchbase.client.java.Bucket; import com.couchbase.client.java.document.JsonDocument; import com.couchbase.client.java.document.json.JsonObject; -import io.reactivity.core.broadcaster.config.CouchbaseConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -44,7 +43,7 @@ * @since 0.1.0 */ @RunWith(SpringRunner.class) -@ContextConfiguration(classes = CouchbaseConfig.class) +@ContextConfiguration(classes = TestConfig.class) public class RepositoryTest { /** diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java new file mode 100644 index 0000000..4d0793d --- /dev/null +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java @@ -0,0 +1,48 @@ +/* + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster; + +import io.reactivity.core.broadcaster.config.CouchbaseConfig; +import org.springframework.context.annotation.Configuration; + +/** + *

    + * This configuration class detects environment variable. + * This kind of support is provided by spring boot but the {@code SpringBootApplication} annotation is currently not used. + *

    + * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@Configuration +public class TestConfig extends CouchbaseConfig { + + /** + *

    + * Builds a the default instance. + *

    + */ + public TestConfig() { + final String nodes = System.getenv("REACTIVITY_COUCHBASE_NODES"); + + if (nodes != null) { + setNodes(nodes.split(",")); + } + } +} diff --git a/core/java-lib/pom.xml b/core/java-lib/pom.xml index fe9418c..9f58d6b 100644 --- a/core/java-lib/pom.xml +++ b/core/java-lib/pom.xml @@ -8,13 +8,17 @@ 0.1.0-SNAPSHOT jar - Reactiviy Java Library + Reactivity core Java Library Library containing common dependencies between Java Microservices + + io.reactivity.core + reactivity-parent + 0.1.0-SNAPSHOT + ../pom.xml + + - UTF-8 - UTF-8 - 1.8 2.8.5 3.0.1 @@ -51,14 +55,6 @@ false
    - - org.apache.maven.plugins - maven-compiler-plugin - - ${java.version} - ${java.version} - -
    diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..a6c0fdd --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + io.reactivity.core + reactivity-parent + 0.1.0-SNAPSHOT + pom + + + UTF-8 + UTF-8 + 1.8 + + + github + 0.12 + + + + + + internal.repo + GitHub Repository + file://${project.build.directory}/mvn-repo + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + + com.github.github + site-maven-plugin + ${site-maven-plugin.version} + + + deploy: maven artifact for ${project.name} version ${project.version} + + + ${project.build.directory}/mvn-repo + **/* + + + maven-repository + reactivity-io + refs/heads/mvn-repo + true + + + + + + site + + deploy + + + + + + \ No newline at end of file From 786fae34ed456bcd91d01cdc68bcbd38970b1657 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 26 Nov 2016 14:50:55 +0100 Subject: [PATCH 006/132] jar: package an executable and working fat JAR --- core/broadcaster/pom.xml | 7 +++++++ .../MockCouchbaseReactivityRepository.java | 9 ++++++++- .../core/broadcaster/web/MockController.java | 16 ++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 4393d6b..78fb4a2 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -114,6 +114,13 @@ org.springframework.boot spring-boot-maven-plugin + + + + repackage + + + diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java index c46c426..b6569ce 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/MockCouchbaseReactivityRepository.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Repository; import org.springframework.util.FileCopyUtils; import rx.Observable; @@ -52,6 +53,12 @@ @Repository public class MockCouchbaseReactivityRepository extends CouchbaseReactvityRepository { + /** + * Resource loader. + */ + @Autowired + private ResourceLoader resourceLoader; + /** *

    * Builds a new repository. @@ -113,7 +120,7 @@ private long nexMockTimestamp() { } private String loadMockedImage(final String mockName) { - final Resource cpr = new ClassPathResource("mocks/" + mockName); + final Resource cpr = resourceLoader.getResource("classpath:mocks/" + mockName); final byte[] picture; try (final InputStream is = cpr.getInputStream()) { diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java index 673f952..6da0d3c 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/MockController.java @@ -1,6 +1,7 @@ package io.reactivity.core.broadcaster.web; -import org.springframework.core.io.ClassPathResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -23,6 +24,17 @@ @RestController public class MockController { + /** + * Path to index.html file. + */ + private final String INDEX = "classpath:mocks/index.html"; + + /** + * Resource loader. + */ + @Autowired + private ResourceLoader resourceLoader; + /** *

    * Generates a test index.html page with a new cookie to track the user session. @@ -38,7 +50,7 @@ public ResponseEntity index() throws IOException { responseHeaders.add("Set-Cookie", "SESSION=" + sessionId); responseHeaders.set("Content-Type", "text/html"); - try (InputStreamReader isr = new InputStreamReader(new ClassPathResource("mocks/index.html").getInputStream())) { + try (InputStreamReader isr = new InputStreamReader(resourceLoader.getResource(INDEX).getInputStream())) { return new ResponseEntity<>(FileCopyUtils.copyToString(isr), responseHeaders, HttpStatus.OK); } } From e4adf52b3ae3ef348e4cf0abbe07d8b2d31ebaec Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 28 Nov 2016 14:57:23 +0100 Subject: [PATCH 007/132] git: add .gitignore with common IDE metadata --- .gitignore | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2af7cef --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file From 8edf386b78646e1d1cb0e0c7bb3b76a2cc032d70 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 28 Nov 2016 15:00:33 +0100 Subject: [PATCH 008/132] dependencies: delegate netty version management --- core/broadcaster/pom.xml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 78fb4a2..f326057 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -22,7 +22,7 @@ 1.1.9 1.1.1 3.0.3.RELEASE - 0.5.2.RELEASE + 2.0.0.BUILD-SNAPSHOT 0.1.0.BUILD-SNAPSHOT 0.6.0.BUILD-SNAPSHOT @@ -33,7 +33,14 @@ org.springframework.boot spring-boot-dependencies - 2.0.0.BUILD-SNAPSHOT + ${spring-boot-dependencies.version} + pom + import + + + org.springframework.boot.experimental + spring-boot-dependencies-web-reactive + ${spring-web-reactive.version} pom import @@ -41,8 +48,8 @@ - - + + org.springframework.data spring-data-commons @@ -75,7 +82,7 @@ ${project.version} - + io.projectreactor reactor-core @@ -84,12 +91,10 @@ io.projectreactor.ipc reactor-netty - ${reactor-netty.version} org.springframework.boot.experimental spring-boot-starter-web-reactive - ${spring-web-reactive.version} org.springframework.boot From 83f42037e579bf590b829c47ea0fd68ea57664ba Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 28 Nov 2016 15:01:39 +0100 Subject: [PATCH 009/132] reactive-streams: log any throwable --- .../core/broadcaster/service/FluxDecorator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java index e140196..0bb4e7f 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java @@ -24,6 +24,8 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import rsc.publisher.PublisherJust; @@ -48,6 +50,11 @@ public class FluxDecorator { */ private static final Duration TIMEOUT = Duration.ofSeconds(10); + /** + * Logger. + */ + private final Logger logger = LoggerFactory.getLogger(getClass()); + /** *

    * Method interceptor that decorates the returned {@code Flux} with a {@link #TIMEOUT timeout} handler, @@ -79,6 +86,7 @@ public Flux> decorate(final ProceedingJoinPoin */ Flux> decorate(final Flux> flux, final String log) { return flux.timeout(TIMEOUT, PublisherJust.fromCallable(Error::timeoutEvent)) + .doOnError(t -> logger.error("Intercepted Flux error", t)) .switchOnError(PublisherJust.fromCallable(Error::exceptionEvent)) .log(log); } From c66999cdd5df1ab0899acc94f9b951aeb26320f9 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 28 Nov 2016 15:02:43 +0100 Subject: [PATCH 010/132] ci: deploy to cloud foundry --- .travis.yml | 17 ++++++++++++++++- core/broadcaster/pom.xml | 9 +++++++++ .../src/main/resources/mocks/index.html | 6 +++--- manifest.yml | 5 +++++ 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 manifest.yml diff --git a/.travis.yml b/.travis.yml index 4c54812..726734b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,19 @@ env: - MODULE=core/broadcaster before_install: - cp config/settings.xml ~/.m2/settings.xml -script: cd $MODULE && mvn clean deploy \ No newline at end of file + - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc +script: + - mvn -f $MODULE/pom.xml clean deploy +deploy: +- provider: cloudfoundry + api: https://api.run.pivotal.io + username: $CF_DEPLOY_USERNAME + password: + secure: RE8snuhO5HKpV8RUPpVXP7KTaqSJb3DS4FSIzaCMy/+N54Uqvnk3ecFyXGJpgYSCRmiZEohP9hXSGYaRX2aXOQvnxtnRcH73X0J8c5T1o3gvLKUdlyZg2HIEMUu7x2pVekJqe300hlhi5Uxgm2MLjdFD74xhoN7USKnBdXOF/cyjyIomEr4HX8UCxMxjbgeAyGXvPyrH1bQbp6j9QdZy06zpMD0+MYzl66qYV4pgxeey/jyqh+ldAQOtXqXueIGG3PQr+zFQTX9Jnw7ilKvP9HqTAngt32GaccB+zCmMhmKFouodJzKtFnpGOQ6d82XgdKMcZcZfGK9zMI1ZDVLWrftfjap+8TFuHpiJbghj52bGrMAD0/+d14LxsSVj0NyuQ7a/7RwalfF9aZOs1CNrLO9KkKy+5ncUKv6dLZtBZ9bN9+bwdb3pfS78C7JaJQOkv6jjXYIJv/yssBla90gfXJY+htnax+5Owss391JYsjhKFtbYL9LhEe587t5lsz7tkOygRPQ+zARInmJt6DBc/rHdtLDMOdhEaA+MKt9Sfn2TZuTXKsu3/pbM95+061u3wmZl7jxqjhnfMhHn1JkD0CkmWxbKLbL9ELy99TvySjdaT+qVar5AXSpL3iyLX/APjFz0RA+1jO8kOlfYebFZYNwoClJG7oTw9XcUuePGb7Q= + organization: Reactivity + space: development + manifest: manifest.yml + on: + repo: reactivity-io/reactivity + branch: travis-deploy + condition: $MODULE = 'core/broadcaster' diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index f326057..a07fc1f 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -116,6 +116,15 @@ + + com.github.github + site-maven-plugin + + + true + + + org.springframework.boot spring-boot-maven-plugin diff --git a/core/broadcaster/src/main/resources/mocks/index.html b/core/broadcaster/src/main/resources/mocks/index.html index 75091dd..f2529b8 100644 --- a/core/broadcaster/src/main/resources/mocks/index.html +++ b/core/broadcaster/src/main/resources/mocks/index.html @@ -21,7 +21,7 @@ addEvent("click", document.getElementById('more'), more); var req = new XMLHttpRequest(); - req.open('GET', 'http://' + document.domain + ':8080/load/organizations', false); + req.open('GET', '/load/organizations', false); req.send(null); JSON.parse(req.responseText).forEach(onEvent); @@ -41,7 +41,7 @@ function more() { var req = new XMLHttpRequest(); - req.open('GET', 'http://' + document.domain + ':8080/load/artifacts/' + viewId + '/limit/1/maxage/' + (lastArtifact - 1), false); + req.open('GET', '/load/artifacts/' + viewId + '/limit/1/maxage/' + (lastArtifact - 1), false); req.send(null); JSON.parse(req.responseText).forEach(onEvent); @@ -49,7 +49,7 @@ function subscribe() { var req = new XMLHttpRequest(); - req.open('GET', 'http://' + document.domain + ':8080/subscribe/1', false); + req.open('GET', '/subscribe/1', false); console.log("loading"); req.send(null); diff --git a/manifest.yml b/manifest.yml new file mode 100644 index 0000000..b26f47d --- /dev/null +++ b/manifest.yml @@ -0,0 +1,5 @@ +applications: + - name: reactivity-broadcaster + memory: 512M + random-route: true + path: core/broadcaster/target/broadcaster-0.1.0-SNAPSHOT.jar \ No newline at end of file From 94cbcf9bc9168457c15e7226d4b0901edc44ec24 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Wed, 4 Jan 2017 18:11:16 +0100 Subject: [PATCH 011/132] log: use log4j2 as logging framework Using log4j2 requires to upgrade reactor-core to version 3.0.4 as it provides a fix for a compatibility issue between the two libraries. --- core/broadcaster/pom.xml | 17 ++++++++++++++++- core/broadcaster/src/main/resources/log4j2.xml | 13 +++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 core/broadcaster/src/main/resources/log4j2.xml diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index a07fc1f..069ced2 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -21,7 +21,7 @@ 2.3.4 1.1.9 1.1.1 - 3.0.3.RELEASE + 3.0.4.RELEASE 2.0.0.BUILD-SNAPSHOT 0.1.0.BUILD-SNAPSHOT 0.6.0.BUILD-SNAPSHOT @@ -108,6 +108,21 @@ spring-boot-starter-aop + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + org.springframework.boot spring-boot-starter-test diff --git a/core/broadcaster/src/main/resources/log4j2.xml b/core/broadcaster/src/main/resources/log4j2.xml new file mode 100644 index 0000000..0a2b259 --- /dev/null +++ b/core/broadcaster/src/main/resources/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file From 6705b31966a2694b4c08e6c66acbb1bb8879b0e9 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 6 Jan 2017 15:23:10 +0100 Subject: [PATCH 012/132] front: init structure of the apps :) --- front/.eslintrc | 27 + front/.gitignore | 4 + front/README.md | 19 + front/bower.json | 22 + front/gulpfile.js | 28 + front/index.html | 44 + front/manifest.json | 10 + front/package.json | 22 + front/src/components/app-card.html | 1440 +++++++++++++++++ front/src/components/app-orga.html | 226 +++ front/src/components/app-orga2.html | 143 ++ front/src/components/app-view.html | 24 + front/src/reactivity-shell.html | 61 + front/src/shell/navbar/idea.png | Bin 0 -> 112692 bytes front/src/shell/navbar/navbar.html | 68 + front/src/shell/navbar/navbar.js | 13 + front/src/shell/pages/404/polymerosaurus.png | Bin 0 -> 8469 bytes front/src/shell/pages/404/view-404.html | 77 + .../pages/organisation/organisation.html | 10 + .../shell/pages/organisation/organisation.js | 33 + .../pages/organisations/organisations.html | 43 + .../pages/organisations/organisations.js | 28 + front/src/shell/pages/settings/settings.html | 15 + front/src/shell/pages/settings/settings.js | 6 + front/src/shell/pages/views-wrapper.html | 96 ++ front/src/utils/eventSource.util.js | 77 + 26 files changed, 2536 insertions(+) create mode 100644 front/.eslintrc create mode 100644 front/.gitignore create mode 100644 front/README.md create mode 100644 front/bower.json create mode 100644 front/gulpfile.js create mode 100644 front/index.html create mode 100644 front/manifest.json create mode 100644 front/package.json create mode 100644 front/src/components/app-card.html create mode 100644 front/src/components/app-orga.html create mode 100644 front/src/components/app-orga2.html create mode 100644 front/src/components/app-view.html create mode 100644 front/src/reactivity-shell.html create mode 100644 front/src/shell/navbar/idea.png create mode 100644 front/src/shell/navbar/navbar.html create mode 100644 front/src/shell/navbar/navbar.js create mode 100644 front/src/shell/pages/404/polymerosaurus.png create mode 100644 front/src/shell/pages/404/view-404.html create mode 100644 front/src/shell/pages/organisation/organisation.html create mode 100644 front/src/shell/pages/organisation/organisation.js create mode 100644 front/src/shell/pages/organisations/organisations.html create mode 100644 front/src/shell/pages/organisations/organisations.js create mode 100644 front/src/shell/pages/settings/settings.html create mode 100644 front/src/shell/pages/settings/settings.js create mode 100644 front/src/shell/pages/views-wrapper.html create mode 100644 front/src/utils/eventSource.util.js diff --git a/front/.eslintrc b/front/.eslintrc new file mode 100644 index 0000000..1cc8ff1 --- /dev/null +++ b/front/.eslintrc @@ -0,0 +1,27 @@ +{ + "parser": "babel-eslint", + "ecmaFeatures": { "modules": true }, + "env": { + "browser": true, + "node": true, + "es6": true + }, + "extends": "airbnb", // supposedly good, ajusting some values below. + "rules": { + "arrow-body-style": ["error", "as-needed"], + "jsx-closing-bracket-location": 0, // doesn't always improve readability + "react/jsx-space-before-closing" : 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], + "comma-dangle": 0, // too much + "no-console": 0, + "spaced-comment": 0, // sucks for code comments + "max-len": ["error", 160], // I don't code on my smartphone, need more than the default 80 + "no-param-reassign": ["error", { "props": false }] + }, + "settings": { + "import/resolve": { + "extensions": [ ".js", ".jsx" ] + }, + "import/parser": "babel-eslint" + } +} \ No newline at end of file diff --git a/front/.gitignore b/front/.gitignore new file mode 100644 index 0000000..e6dd7e0 --- /dev/null +++ b/front/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +/bower_components/ +/.idea/ +/build/ \ No newline at end of file diff --git a/front/README.md b/front/README.md new file mode 100644 index 0000000..a873cb6 --- /dev/null +++ b/front/README.md @@ -0,0 +1,19 @@ +## Reactivity web-front + +## Dev +### Install Dependencies + + +#### /!\ some dep need ssh key on github (windows shell can't open passphrase prompt !) /!\ +```sh +npm install +``` + +### Launch server with panel +```sh +npm start +``` + +### This apps need a server to init SSE connection an provide data. + +plz check the express-sse-with-electron-panel project in the same repo. \ No newline at end of file diff --git a/front/bower.json b/front/bower.json new file mode 100644 index 0000000..b61ae35 --- /dev/null +++ b/front/bower.json @@ -0,0 +1,22 @@ +{ + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "author": "Nathan DAMIE", + "dependencies": { + "polymer": "Polymer/polymer#2.0-preview", + "paper-card": "PolymerElements/paper-card#2.0-preview", + "iron-icons": "PolymerElements/iron-icons#2.0-preview", + "iron-icon": "PolymerElements/iron-icon#2.0-preview", + "iron-flex-layout": "2.0-preview", + "iron-collapse": "2.0-preview", + "paper-button": "2.0-preview", + "paper-checkbox": "2.0-preview", + "paper-icon-button": "2.0-preview", + "app-router": "blasten/app-router#master" + }, + "resolutions": { + "polymer": "2.0-preview", + "webcomponentsjs": "v1" + } +} diff --git a/front/gulpfile.js b/front/gulpfile.js new file mode 100644 index 0000000..2218eab --- /dev/null +++ b/front/gulpfile.js @@ -0,0 +1,28 @@ +const gulp = require('gulp'); +const browserSync = require('browser-sync').create(); +const proxyMiddleware = require('http-proxy-middleware'); +const historyApiFallback = require('connect-history-api-fallback'); + +const options = { + target: 'http://localhost:8080', + changeOrigin: true, + pathRewrite: { + '^/api': '/' + } +}; + +// Watch scss AND html files, doing different things with each. +gulp.task('default', () => { + // Serve files from the root of this project + browserSync.init({ + server: { + baseDir: "./", + index: "index.html", + middleware: [ + historyApiFallback(), + proxyMiddleware('/api', options) + ] + } + }); +}); + diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..4c628fa --- /dev/null +++ b/front/index.html @@ -0,0 +1,44 @@ + + + + + + + Reactivity + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/manifest.json b/front/manifest.json new file mode 100644 index 0000000..40c8fba --- /dev/null +++ b/front/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Reactivity", + "short_name": "Reactivity", + "display": "standalone", + "theme_color": "#3f51b5", + "background_color": "#3f51b5", + "icons": [ + + ] +} diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..d40c3da --- /dev/null +++ b/front/package.json @@ -0,0 +1,22 @@ +{ + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "main": "main.js", + "scripts": { + "postinstall": "bower install", + "start": "gulp", + "lint": "./node_modules/.bin/eslint ." + }, + "author": "Nathan DAMIE", + "devDependencies": { + "bower": "^1.8.0", + "browser-sync": "^2.18.5", + "connect-history-api-fallback": "^1.3.0", + "gulp": "github:gulpjs/gulp#4.0", + "http-proxy-middleware": "^0.17.3" + }, + "dependencies": { + "lodash": "^4.17.4" + } +} diff --git a/front/src/components/app-card.html b/front/src/components/app-card.html new file mode 100644 index 0000000..4c944d8 --- /dev/null +++ b/front/src/components/app-card.html @@ -0,0 +1,1440 @@ + + + + + + + diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html new file mode 100644 index 0000000..a784c30 --- /dev/null +++ b/front/src/components/app-orga.html @@ -0,0 +1,226 @@ + + + + + + + diff --git a/front/src/components/app-orga2.html b/front/src/components/app-orga2.html new file mode 100644 index 0000000..1d402a5 --- /dev/null +++ b/front/src/components/app-orga2.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + diff --git a/front/src/components/app-view.html b/front/src/components/app-view.html new file mode 100644 index 0000000..3e10e5c --- /dev/null +++ b/front/src/components/app-view.html @@ -0,0 +1,24 @@ + + + + + + + diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html new file mode 100644 index 0000000..d2ac8df --- /dev/null +++ b/front/src/reactivity-shell.html @@ -0,0 +1,61 @@ + + + + + + + + + + diff --git a/front/src/shell/navbar/idea.png b/front/src/shell/navbar/idea.png new file mode 100644 index 0000000000000000000000000000000000000000..e65b6587696a80aa728b2cb0a7b37cbb46ea2c50 GIT binary patch literal 112692 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%0A+70|6Jra*EdGmbSDu= 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�-|)VK`U%Fow=|W)Ua3@WH4Pw=&a} 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 + + + + diff --git a/front/src/shell/navbar/navbar.js b/front/src/shell/navbar/navbar.js new file mode 100644 index 0000000..7ca67b6 --- /dev/null +++ b/front/src/shell/navbar/navbar.js @@ -0,0 +1,13 @@ +class ShellNavbar extends Polymer.Element { + static get is() { + return 's-navbar'; + } + + handleClick (e) { + e.preventDefault(); + document.dispatchEvent(new CustomEvent('nav',{detail: {path:e.currentTarget.pathname}})); + } +} + +// Register custom element definition using standard platform API +customElements.define(ShellNavbar.is, ShellNavbar); \ No newline at end of file diff --git a/front/src/shell/pages/404/polymerosaurus.png b/front/src/shell/pages/404/polymerosaurus.png new file mode 100644 index 0000000000000000000000000000000000000000..28201c9ba3869a13d49a07cffd8831d9b089a8f0 GIT binary patch literal 8469 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

      +}=) zzpOGB?do^?Nf*DM#)<|>0T_SbndmO-T!DRz*~N3F48KYq9vz%~0R3~}rJMb-D^uHB zno~Rc<#BvFFvM!yVJMn%HnHN(yJvce7}=SFbrHT7X}KSC{YCPkf}M^=KOJ`Ve!D+B zVN>^hw)_0a_V~&EpJ$(r=KcgBQn6@zRw04=5D>nr3D zlX{cAK*aun+Q}$JV%3e>Os*;ZuC$#H-U~V64xPUv$oZCr$|+|rt-nz4SORxN7wdG@ z=+g-yd7E#jcJ+>H!SPR_;H`GZh>uI%vJCe44`0aMKh*K^+_6mH+mt7qL0_u6%5oo~ zWVF*RfxE_XowLZ2ss3Y;pLKqALe?$0Puov?Qr13Ib$5t53JK*}fG;J2{a>tJuRgIJ zW7M9@Jgbtvy^xJIVj1|-FCeugog=Wp_E}XT{p(sy1@DSTzSOek_+EkUn+i2c)lN-?SNP5^2;-Q)sxF6?Na_ z;(_G;&AzSWUX9}l@+_-6s#pXA(S~ZNb9YnxjJ4^v%#i%-YhJ^P5r>FTlE=%ONALLM oep{M89neq+t=OCFKNh+pc{QJ5jJNRguLTA@ZDXwp4f~h>2eZ8|ZvX%Q literal 0 HcmV?d00001 diff --git a/front/src/shell/pages/404/view-404.html b/front/src/shell/pages/404/view-404.html new file mode 100644 index 0000000..8cab5bc --- /dev/null +++ b/front/src/shell/pages/404/view-404.html @@ -0,0 +1,77 @@ + + + + + + diff --git a/front/src/shell/pages/organisation/organisation.html b/front/src/shell/pages/organisation/organisation.html new file mode 100644 index 0000000..aca70c6 --- /dev/null +++ b/front/src/shell/pages/organisation/organisation.html @@ -0,0 +1,10 @@ + + + + + diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js new file mode 100644 index 0000000..e0b97fd --- /dev/null +++ b/front/src/shell/pages/organisation/organisation.js @@ -0,0 +1,33 @@ +class ShellOrganisation extends Polymer.Element { + static get is() { + return 's-organisation'; + } + + connectedCallback() { + super.connectedCallback(); + + if(this.id) { + var req = new XMLHttpRequest(); + req.open('GET', `http://localhost:3000/api/subscribe/${this.id}`, true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if (req.status == 200) { + this.data = JSON.parse(req.responseText); + var groups = _.groupBy(this.data, "event"); + + + //TODO:DO something with that !!! {{info}} + + + + } else { + alert("Erreur pendant le chargement de la page.\n"); + } + } + }; + req.send(null); + } + } +} +// Register custom element definition using standard platform API +customElements.define(ShellOrganisation.is, ShellOrganisation); \ No newline at end of file diff --git a/front/src/shell/pages/organisations/organisations.html b/front/src/shell/pages/organisations/organisations.html new file mode 100644 index 0000000..12acf9d --- /dev/null +++ b/front/src/shell/pages/organisations/organisations.html @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js new file mode 100644 index 0000000..4cd8b81 --- /dev/null +++ b/front/src/shell/pages/organisations/organisations.js @@ -0,0 +1,28 @@ +class ShellOrganisations extends Polymer.Element { + static get is() { + return 's-organisations'; + } + + constructor() { + super(); + this.organisations = []; + } + + connectedCallback() { + super.connectedCallback(); + var req = new XMLHttpRequest(); + req.open('GET', 'http://localhost:3000/api/load/organizations', true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if(req.status == 200) { + this.organisations = JSON.parse(req.responseText); + } else { + alert("Don't be silly launch the server !\n"); + } + } + }; + req.send(null); + } +} +// Register custom element definition using standard platform API +customElements.define(ShellOrganisations.is, ShellOrganisations); \ No newline at end of file diff --git a/front/src/shell/pages/settings/settings.html b/front/src/shell/pages/settings/settings.html new file mode 100644 index 0000000..797cde3 --- /dev/null +++ b/front/src/shell/pages/settings/settings.html @@ -0,0 +1,15 @@ + + + + diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js new file mode 100644 index 0000000..b60a0aa --- /dev/null +++ b/front/src/shell/pages/settings/settings.js @@ -0,0 +1,6 @@ +class ShellSettings extends Polymer.Element { + static get is() { + return 's-settings'; + } +} +customElements.define(ShellSettings.is, ShellSettings); \ No newline at end of file diff --git a/front/src/shell/pages/views-wrapper.html b/front/src/shell/pages/views-wrapper.html new file mode 100644 index 0000000..77786e8 --- /dev/null +++ b/front/src/shell/pages/views-wrapper.html @@ -0,0 +1,96 @@ + + + + + + + + + diff --git a/front/src/utils/eventSource.util.js b/front/src/utils/eventSource.util.js new file mode 100644 index 0000000..c7d82e0 --- /dev/null +++ b/front/src/utils/eventSource.util.js @@ -0,0 +1,77 @@ +window.EventSourceManager = class EventSourceManager { + constructor(url, manager) { + //Create an Observable for the EventSource Connection + this.sourceObservable = Rx.Observable.create((observer) => { + this.source = new EventSource(url); + + const onOpen = (e) => { + observer.onNext(e); + console.log("%c SSE reconnected", "font-size:22px;text-shadow: 0 0 3px #FF0000, 0 0 5px blue;"); + + // this.source.removeEventListener('open', onOpen, false); + }; + + //TODO : On error subscribe to a new stream (wso ) + const onError = (e) => { + if (e.eventPhase === EventSource.CLOSED) { + observer.onCompleted(); + } else { + observer.onError(e); + } + }; + + const onMessage = (e) => { + observer.onNext(e); + }; + + this.source.addEventListener('open', onOpen, false); + this.source.addEventListener('error', onError, false); + this.source.addEventListener('message', onMessage, false); + + //Add listener on specific eventType + this.addEventsObservables(manager); + + return () => { + this.source.removeEventListener('error', onError, false); + this.source.removeEventListener('message', onMessage, false); + this.source.close(); + }; + }); + } + + static get source() { + return this.source; + } + + static get sourceObservable() { + return this.sourceObservable; + } + + /** + * Create an observable for each property of the given manager + * @param {Object} manager + * @returns {void} + */ + addEventsObservables(manager) { + //for each property + for (let eventType in manager) { + if (manager.hasOwnProperty(eventType)) { + + //Create a new observable + const observable = Rx.Observable.create((observer) => { + const onMessage = (e) => { + observer.onNext(e); + }; + + //listen the eventType on the EventSource + this.source.addEventListener(eventType, onMessage, false); + }); + + //Subscribe the callback. + observable.subscribe((e) => { + manager[eventType](e); + }); + } + } + } +}; \ No newline at end of file From 6510f53f934216f57ffbf15e61f31244b9398e94 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 9 Jan 2017 11:19:02 +0100 Subject: [PATCH 013/132] period view: ignore 'from' and 'to' if they are negative --- .../couchbase/ListArtifactViewQuery.java | 16 ++++++++++++++-- .../io/reactivity/core/lib/event/Period.java | 3 ++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java index e8655b4..27d6978 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/repository/couchbase/ListArtifactViewQuery.java @@ -84,11 +84,11 @@ public Observable query(final AsyncBucket bucket) { Expression expression = Expression.x("organization").eq(Expression.x("$organization")); - if (period.getFrom() != null) { + if (accept(period.getFrom())) { expression = expression.and(Expression.x(timestampField).gte(period.getFrom())); } - if (period.getTo() != null) { + if (accept(period.getTo())) { expression = expression.and(Expression.x(timestampField).lte(period.getTo())); } @@ -123,4 +123,16 @@ private Artifact toArtifact(final AsyncN1qlQueryRow row) { Collections.singletonList(view.getId()), o.getObject("categories").toMap()); } + + /** + *

      + * Indicates if the given {@code Long} is not {@code null} and not negative. + *

      + * + * @param value long value + * @return {@code true} if the value is greater or equals to 0, {@code false} otherwise + */ + private boolean accept(final Long value) { + return value != null && value.compareTo(0L) > -1; + } } diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java index 38c61f9..68348fa 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Period.java @@ -24,7 +24,8 @@ *

      * A period of time defined by a {@link ArtifactView}. Artifacts can be selected by defining a range of timestamps. * One of the two timestamps (the first corresponding to the oldest artifact and the second to the newest artifact) - * can be {@code null}. A limit of returned artifact can be specified. Otherwise, artifacts are selected without limit. + * can be {@code null} or negative to be ignored. A limit of returned artifact can be specified. + * Otherwise, artifacts are selected without limit. * * @author Guillaume DROUET * @since 0.1.0 From 6a1eb96f5a91d189a476cc6f621b871492c27f8a Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 9 Jan 2017 11:36:24 +0100 Subject: [PATCH 014/132] config: define external property for couchbase bucket --- .../broadcaster/config/CouchbaseConfig.java | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 082b7b7..1490e09 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -22,6 +22,13 @@ * This configuration initializes couchbase connection. *

      * + *

      + * See the class constants for properties that can be configured and their default values. Node that properties can be + * configured in several ways thanks to Spring Boot property resolution. For instance, you can run the application with + * program argument {@code --reactivity.couchbase.nodes=xxx} to define the {@code reactivity.couchbase.nodes} property. + * More details here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html + *

      + * * @author Guillaume DROUET * @since 0.1.0 */ @@ -29,7 +36,25 @@ @ConfigurationProperties(prefix = "reactivity.couchbase") public class CouchbaseConfig { - private String[] nodes = new String[] { "127.0.0.1" } ; + /** + * Default value for property {@code reactivity.couchbase.nodes}. + */ + public static final String DEFAULT_COUCHBASE_NODES = "127.0.0.1"; + + /** + * Default value for property {@code reactivity.couchbase.bucket}. + */ + public static final String DEFAULT_COUCHBASE_BUCKET = "artifact"; + + /** + * Nodes to use. See {@link #DEFAULT_COUCHBASE_NODES default} value. + */ + private String[] nodes = new String[] { DEFAULT_COUCHBASE_NODES } ; + + /** + * Bucket name. See {@link #DEFAULT_COUCHBASE_BUCKET default} value. + */ + private String bucket = DEFAULT_COUCHBASE_BUCKET; /** *

      @@ -41,7 +66,7 @@ public class CouchbaseConfig { @Bean Bucket sync() { final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); - final Bucket bucket = cluster.openBucket("artifact"); + final Bucket bucket = cluster.openBucket(this.bucket); bucket.bucketManager().createN1qlPrimaryIndex(true, false); return bucket; @@ -57,4 +82,15 @@ Bucket sync() { public void setNodes(final String[] nodes) { this.nodes = nodes; } + + /** + *

      + * Sets the bucket where data are managed. + *

      + * + * @param bucket the bucket name + */ + public void setBucket(final String bucket) { + this.bucket = bucket; + } } From e89b7a0b875f802baaf7c2018f09b17f10df5e8a Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 9 Jan 2017 12:09:44 +0100 Subject: [PATCH 015/132] reactive-module: add profiles to chose between tomcat and netty reactive support Netty remains the default selection but activating the tomcat profile will use tomcat server instead of netty. --- core/broadcaster/pom.xml | 51 ++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 069ced2..582f606 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -82,26 +82,12 @@ ${project.version} - + io.projectreactor reactor-core ${reactor-core.version} - - io.projectreactor.ipc - reactor-netty - - - org.springframework.boot.experimental - spring-boot-starter-web-reactive - - - org.springframework.boot - spring-boot-starter-tomcat - - - org.springframework.boot @@ -154,6 +140,41 @@ + + + + netty + + true + + + + io.projectreactor.ipc + reactor-netty + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + + tomcat + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + + + + + From 688d63f57e16fe54fc0e60018ce52774ed5deb6e Mon Sep 17 00:00:00 2001 From: gdrouet Date: Mon, 9 Jan 2017 12:13:44 +0100 Subject: [PATCH 016/132] plugins: fix spring-boot-maven-plugin version --- core/broadcaster/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 582f606..261ee2c 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -129,6 +129,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring-boot-dependencies.version} From b20caa2b4199fdd8bae2ee9261a4338bf85ea408 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 6 Jan 2017 15:23:10 +0100 Subject: [PATCH 017/132] front: init structure of the apps :) --- front/.eslintrc | 27 + front/.gitignore | 4 + front/README.md | 19 + front/bower.json | 22 + front/gulpfile.js | 28 + front/index.html | 44 + front/manifest.json | 10 + front/package.json | 22 + front/src/components/app-card.html | 1440 +++++++++++++++++ front/src/components/app-orga.html | 226 +++ front/src/components/app-orga2.html | 143 ++ front/src/components/app-view.html | 24 + front/src/reactivity-shell.html | 61 + front/src/shell/navbar/idea.png | Bin 0 -> 112692 bytes front/src/shell/navbar/navbar.html | 68 + front/src/shell/navbar/navbar.js | 13 + front/src/shell/pages/404/polymerosaurus.png | Bin 0 -> 8469 bytes front/src/shell/pages/404/view-404.html | 77 + .../pages/organisation/organisation.html | 10 + .../shell/pages/organisation/organisation.js | 33 + .../pages/organisations/organisations.html | 43 + .../pages/organisations/organisations.js | 28 + front/src/shell/pages/settings/settings.html | 15 + front/src/shell/pages/settings/settings.js | 6 + front/src/shell/pages/views-wrapper.html | 96 ++ front/src/utils/eventSource.util.js | 77 + 26 files changed, 2536 insertions(+) create mode 100644 front/.eslintrc create mode 100644 front/.gitignore create mode 100644 front/README.md create mode 100644 front/bower.json create mode 100644 front/gulpfile.js create mode 100644 front/index.html create mode 100644 front/manifest.json create mode 100644 front/package.json create mode 100644 front/src/components/app-card.html create mode 100644 front/src/components/app-orga.html create mode 100644 front/src/components/app-orga2.html create mode 100644 front/src/components/app-view.html create mode 100644 front/src/reactivity-shell.html create mode 100644 front/src/shell/navbar/idea.png create mode 100644 front/src/shell/navbar/navbar.html create mode 100644 front/src/shell/navbar/navbar.js create mode 100644 front/src/shell/pages/404/polymerosaurus.png create mode 100644 front/src/shell/pages/404/view-404.html create mode 100644 front/src/shell/pages/organisation/organisation.html create mode 100644 front/src/shell/pages/organisation/organisation.js create mode 100644 front/src/shell/pages/organisations/organisations.html create mode 100644 front/src/shell/pages/organisations/organisations.js create mode 100644 front/src/shell/pages/settings/settings.html create mode 100644 front/src/shell/pages/settings/settings.js create mode 100644 front/src/shell/pages/views-wrapper.html create mode 100644 front/src/utils/eventSource.util.js diff --git a/front/.eslintrc b/front/.eslintrc new file mode 100644 index 0000000..1cc8ff1 --- /dev/null +++ b/front/.eslintrc @@ -0,0 +1,27 @@ +{ + "parser": "babel-eslint", + "ecmaFeatures": { "modules": true }, + "env": { + "browser": true, + "node": true, + "es6": true + }, + "extends": "airbnb", // supposedly good, ajusting some values below. + "rules": { + "arrow-body-style": ["error", "as-needed"], + "jsx-closing-bracket-location": 0, // doesn't always improve readability + "react/jsx-space-before-closing" : 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], + "comma-dangle": 0, // too much + "no-console": 0, + "spaced-comment": 0, // sucks for code comments + "max-len": ["error", 160], // I don't code on my smartphone, need more than the default 80 + "no-param-reassign": ["error", { "props": false }] + }, + "settings": { + "import/resolve": { + "extensions": [ ".js", ".jsx" ] + }, + "import/parser": "babel-eslint" + } +} \ No newline at end of file diff --git a/front/.gitignore b/front/.gitignore new file mode 100644 index 0000000..e6dd7e0 --- /dev/null +++ b/front/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +/bower_components/ +/.idea/ +/build/ \ No newline at end of file diff --git a/front/README.md b/front/README.md new file mode 100644 index 0000000..a873cb6 --- /dev/null +++ b/front/README.md @@ -0,0 +1,19 @@ +## Reactivity web-front + +## Dev +### Install Dependencies + + +#### /!\ some dep need ssh key on github (windows shell can't open passphrase prompt !) /!\ +```sh +npm install +``` + +### Launch server with panel +```sh +npm start +``` + +### This apps need a server to init SSE connection an provide data. + +plz check the express-sse-with-electron-panel project in the same repo. \ No newline at end of file diff --git a/front/bower.json b/front/bower.json new file mode 100644 index 0000000..b61ae35 --- /dev/null +++ b/front/bower.json @@ -0,0 +1,22 @@ +{ + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "author": "Nathan DAMIE", + "dependencies": { + "polymer": "Polymer/polymer#2.0-preview", + "paper-card": "PolymerElements/paper-card#2.0-preview", + "iron-icons": "PolymerElements/iron-icons#2.0-preview", + "iron-icon": "PolymerElements/iron-icon#2.0-preview", + "iron-flex-layout": "2.0-preview", + "iron-collapse": "2.0-preview", + "paper-button": "2.0-preview", + "paper-checkbox": "2.0-preview", + "paper-icon-button": "2.0-preview", + "app-router": "blasten/app-router#master" + }, + "resolutions": { + "polymer": "2.0-preview", + "webcomponentsjs": "v1" + } +} diff --git a/front/gulpfile.js b/front/gulpfile.js new file mode 100644 index 0000000..2218eab --- /dev/null +++ b/front/gulpfile.js @@ -0,0 +1,28 @@ +const gulp = require('gulp'); +const browserSync = require('browser-sync').create(); +const proxyMiddleware = require('http-proxy-middleware'); +const historyApiFallback = require('connect-history-api-fallback'); + +const options = { + target: 'http://localhost:8080', + changeOrigin: true, + pathRewrite: { + '^/api': '/' + } +}; + +// Watch scss AND html files, doing different things with each. +gulp.task('default', () => { + // Serve files from the root of this project + browserSync.init({ + server: { + baseDir: "./", + index: "index.html", + middleware: [ + historyApiFallback(), + proxyMiddleware('/api', options) + ] + } + }); +}); + diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..4c628fa --- /dev/null +++ b/front/index.html @@ -0,0 +1,44 @@ + + + + + + + Reactivity + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/manifest.json b/front/manifest.json new file mode 100644 index 0000000..40c8fba --- /dev/null +++ b/front/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Reactivity", + "short_name": "Reactivity", + "display": "standalone", + "theme_color": "#3f51b5", + "background_color": "#3f51b5", + "icons": [ + + ] +} diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..d40c3da --- /dev/null +++ b/front/package.json @@ -0,0 +1,22 @@ +{ + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "main": "main.js", + "scripts": { + "postinstall": "bower install", + "start": "gulp", + "lint": "./node_modules/.bin/eslint ." + }, + "author": "Nathan DAMIE", + "devDependencies": { + "bower": "^1.8.0", + "browser-sync": "^2.18.5", + "connect-history-api-fallback": "^1.3.0", + "gulp": "github:gulpjs/gulp#4.0", + "http-proxy-middleware": "^0.17.3" + }, + "dependencies": { + "lodash": "^4.17.4" + } +} diff --git a/front/src/components/app-card.html b/front/src/components/app-card.html new file mode 100644 index 0000000..4c944d8 --- /dev/null +++ b/front/src/components/app-card.html @@ -0,0 +1,1440 @@ + + + + + + + diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html new file mode 100644 index 0000000..a784c30 --- /dev/null +++ b/front/src/components/app-orga.html @@ -0,0 +1,226 @@ + + + + + + + diff --git a/front/src/components/app-orga2.html b/front/src/components/app-orga2.html new file mode 100644 index 0000000..1d402a5 --- /dev/null +++ b/front/src/components/app-orga2.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + diff --git a/front/src/components/app-view.html b/front/src/components/app-view.html new file mode 100644 index 0000000..3e10e5c --- /dev/null +++ b/front/src/components/app-view.html @@ -0,0 +1,24 @@ + + + + + + + diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html new file mode 100644 index 0000000..d2ac8df --- /dev/null +++ b/front/src/reactivity-shell.html @@ -0,0 +1,61 @@ + + + + + + + + + + diff --git a/front/src/shell/navbar/idea.png b/front/src/shell/navbar/idea.png new file mode 100644 index 0000000000000000000000000000000000000000..e65b6587696a80aa728b2cb0a7b37cbb46ea2c50 GIT binary patch literal 112692 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%0A+70|6Jra*EdGmbSDu= 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�-|)VK`U%Fow=|W)Ua3@WH4Pw=&a} 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 + + + + diff --git a/front/src/shell/navbar/navbar.js b/front/src/shell/navbar/navbar.js new file mode 100644 index 0000000..7ca67b6 --- /dev/null +++ b/front/src/shell/navbar/navbar.js @@ -0,0 +1,13 @@ +class ShellNavbar extends Polymer.Element { + static get is() { + return 's-navbar'; + } + + handleClick (e) { + e.preventDefault(); + document.dispatchEvent(new CustomEvent('nav',{detail: {path:e.currentTarget.pathname}})); + } +} + +// Register custom element definition using standard platform API +customElements.define(ShellNavbar.is, ShellNavbar); \ No newline at end of file diff --git a/front/src/shell/pages/404/polymerosaurus.png b/front/src/shell/pages/404/polymerosaurus.png new file mode 100644 index 0000000000000000000000000000000000000000..28201c9ba3869a13d49a07cffd8831d9b089a8f0 GIT binary patch literal 8469 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

        +}=) zzpOGB?do^?Nf*DM#)<|>0T_SbndmO-T!DRz*~N3F48KYq9vz%~0R3~}rJMb-D^uHB zno~Rc<#BvFFvM!yVJMn%HnHN(yJvce7}=SFbrHT7X}KSC{YCPkf}M^=KOJ`Ve!D+B zVN>^hw)_0a_V~&EpJ$(r=KcgBQn6@zRw04=5D>nr3D zlX{cAK*aun+Q}$JV%3e>Os*;ZuC$#H-U~V64xPUv$oZCr$|+|rt-nz4SORxN7wdG@ z=+g-yd7E#jcJ+>H!SPR_;H`GZh>uI%vJCe44`0aMKh*K^+_6mH+mt7qL0_u6%5oo~ zWVF*RfxE_XowLZ2ss3Y;pLKqALe?$0Puov?Qr13Ib$5t53JK*}fG;J2{a>tJuRgIJ zW7M9@Jgbtvy^xJIVj1|-FCeugog=Wp_E}XT{p(sy1@DSTzSOek_+EkUn+i2c)lN-?SNP5^2;-Q)sxF6?Na_ z;(_G;&AzSWUX9}l@+_-6s#pXA(S~ZNb9YnxjJ4^v%#i%-YhJ^P5r>FTlE=%ONALLM oep{M89neq+t=OCFKNh+pc{QJ5jJNRguLTA@ZDXwp4f~h>2eZ8|ZvX%Q literal 0 HcmV?d00001 diff --git a/front/src/shell/pages/404/view-404.html b/front/src/shell/pages/404/view-404.html new file mode 100644 index 0000000..8cab5bc --- /dev/null +++ b/front/src/shell/pages/404/view-404.html @@ -0,0 +1,77 @@ + + + + + + diff --git a/front/src/shell/pages/organisation/organisation.html b/front/src/shell/pages/organisation/organisation.html new file mode 100644 index 0000000..aca70c6 --- /dev/null +++ b/front/src/shell/pages/organisation/organisation.html @@ -0,0 +1,10 @@ + + + + + diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js new file mode 100644 index 0000000..e0b97fd --- /dev/null +++ b/front/src/shell/pages/organisation/organisation.js @@ -0,0 +1,33 @@ +class ShellOrganisation extends Polymer.Element { + static get is() { + return 's-organisation'; + } + + connectedCallback() { + super.connectedCallback(); + + if(this.id) { + var req = new XMLHttpRequest(); + req.open('GET', `http://localhost:3000/api/subscribe/${this.id}`, true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if (req.status == 200) { + this.data = JSON.parse(req.responseText); + var groups = _.groupBy(this.data, "event"); + + + //TODO:DO something with that !!! {{info}} + + + + } else { + alert("Erreur pendant le chargement de la page.\n"); + } + } + }; + req.send(null); + } + } +} +// Register custom element definition using standard platform API +customElements.define(ShellOrganisation.is, ShellOrganisation); \ No newline at end of file diff --git a/front/src/shell/pages/organisations/organisations.html b/front/src/shell/pages/organisations/organisations.html new file mode 100644 index 0000000..12acf9d --- /dev/null +++ b/front/src/shell/pages/organisations/organisations.html @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js new file mode 100644 index 0000000..4cd8b81 --- /dev/null +++ b/front/src/shell/pages/organisations/organisations.js @@ -0,0 +1,28 @@ +class ShellOrganisations extends Polymer.Element { + static get is() { + return 's-organisations'; + } + + constructor() { + super(); + this.organisations = []; + } + + connectedCallback() { + super.connectedCallback(); + var req = new XMLHttpRequest(); + req.open('GET', 'http://localhost:3000/api/load/organizations', true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if(req.status == 200) { + this.organisations = JSON.parse(req.responseText); + } else { + alert("Don't be silly launch the server !\n"); + } + } + }; + req.send(null); + } +} +// Register custom element definition using standard platform API +customElements.define(ShellOrganisations.is, ShellOrganisations); \ No newline at end of file diff --git a/front/src/shell/pages/settings/settings.html b/front/src/shell/pages/settings/settings.html new file mode 100644 index 0000000..797cde3 --- /dev/null +++ b/front/src/shell/pages/settings/settings.html @@ -0,0 +1,15 @@ + + + + diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js new file mode 100644 index 0000000..b60a0aa --- /dev/null +++ b/front/src/shell/pages/settings/settings.js @@ -0,0 +1,6 @@ +class ShellSettings extends Polymer.Element { + static get is() { + return 's-settings'; + } +} +customElements.define(ShellSettings.is, ShellSettings); \ No newline at end of file diff --git a/front/src/shell/pages/views-wrapper.html b/front/src/shell/pages/views-wrapper.html new file mode 100644 index 0000000..77786e8 --- /dev/null +++ b/front/src/shell/pages/views-wrapper.html @@ -0,0 +1,96 @@ + + + + + + + + + diff --git a/front/src/utils/eventSource.util.js b/front/src/utils/eventSource.util.js new file mode 100644 index 0000000..c7d82e0 --- /dev/null +++ b/front/src/utils/eventSource.util.js @@ -0,0 +1,77 @@ +window.EventSourceManager = class EventSourceManager { + constructor(url, manager) { + //Create an Observable for the EventSource Connection + this.sourceObservable = Rx.Observable.create((observer) => { + this.source = new EventSource(url); + + const onOpen = (e) => { + observer.onNext(e); + console.log("%c SSE reconnected", "font-size:22px;text-shadow: 0 0 3px #FF0000, 0 0 5px blue;"); + + // this.source.removeEventListener('open', onOpen, false); + }; + + //TODO : On error subscribe to a new stream (wso ) + const onError = (e) => { + if (e.eventPhase === EventSource.CLOSED) { + observer.onCompleted(); + } else { + observer.onError(e); + } + }; + + const onMessage = (e) => { + observer.onNext(e); + }; + + this.source.addEventListener('open', onOpen, false); + this.source.addEventListener('error', onError, false); + this.source.addEventListener('message', onMessage, false); + + //Add listener on specific eventType + this.addEventsObservables(manager); + + return () => { + this.source.removeEventListener('error', onError, false); + this.source.removeEventListener('message', onMessage, false); + this.source.close(); + }; + }); + } + + static get source() { + return this.source; + } + + static get sourceObservable() { + return this.sourceObservable; + } + + /** + * Create an observable for each property of the given manager + * @param {Object} manager + * @returns {void} + */ + addEventsObservables(manager) { + //for each property + for (let eventType in manager) { + if (manager.hasOwnProperty(eventType)) { + + //Create a new observable + const observable = Rx.Observable.create((observer) => { + const onMessage = (e) => { + observer.onNext(e); + }; + + //listen the eventType on the EventSource + this.source.addEventListener(eventType, onMessage, false); + }); + + //Subscribe the callback. + observable.subscribe((e) => { + manager[eventType](e); + }); + } + } + } +}; \ No newline at end of file From ca382409d5d1985f7045b0d4ddffe5cede8f8475 Mon Sep 17 00:00:00 2001 From: nathandm Date: Mon, 9 Jan 2017 16:18:44 +0100 Subject: [PATCH 018/132] front: infinit scroll for "LIST" view type. - impl a lazyloading scroll list - mixin for global var --- core/broadcaster/pom.xml | 52 +--- .../broadcaster/config/CouchbaseConfig.java | 49 +--- .../core/broadcaster/web/EventController.java | 7 +- .../src/main/resources/mocks/index.html | 24 +- front/bower.json | 3 +- front/index.html | 4 +- front/package.json | 3 - front/src/components/app-artifact-list.html | 49 ++++ front/src/components/app-orga.html | 267 ++++++------------ front/src/components/app-orga2.html | 143 ---------- front/src/components/app-view.html | 51 +++- front/src/constants/const.mixin.js | 5 + front/src/reactivity-shell.html | 116 ++++---- front/src/shell/navbar/navbar.html | 2 +- .../pages/organisation/organisation.html | 22 +- .../shell/pages/organisation/organisation.js | 16 +- .../pages/organisations/organisations.html | 22 +- .../pages/organisations/organisations.js | 4 +- front/src/shell/pages/view/view.html | 26 ++ front/src/shell/pages/view/view.js | 59 ++++ front/src/utils/eventSource.util.js | 77 ----- 21 files changed, 421 insertions(+), 580 deletions(-) create mode 100644 front/src/components/app-artifact-list.html delete mode 100644 front/src/components/app-orga2.html create mode 100644 front/src/constants/const.mixin.js create mode 100644 front/src/shell/pages/view/view.html create mode 100644 front/src/shell/pages/view/view.js delete mode 100644 front/src/utils/eventSource.util.js diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 261ee2c..1b187f5 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -82,12 +82,26 @@ ${project.version} - + io.projectreactor reactor-core ${reactor-core.version} + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + + org.springframework.boot @@ -129,7 +143,6 @@ org.springframework.boot spring-boot-maven-plugin - ${spring-boot-dependencies.version} @@ -141,41 +154,6 @@ - - - - netty - - true - - - - io.projectreactor.ipc - reactor-netty - - - org.springframework.boot.experimental - spring-boot-starter-web-reactive - - - org.springframework.boot - spring-boot-starter-tomcat - - - - - - - tomcat - - - org.springframework.boot.experimental - spring-boot-starter-web-reactive - - - - - diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 1490e09..6465318 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -22,13 +22,6 @@ * This configuration initializes couchbase connection. *

        * - *

        - * See the class constants for properties that can be configured and their default values. Node that properties can be - * configured in several ways thanks to Spring Boot property resolution. For instance, you can run the application with - * program argument {@code --reactivity.couchbase.nodes=xxx} to define the {@code reactivity.couchbase.nodes} property. - * More details here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html - *

        - * * @author Guillaume DROUET * @since 0.1.0 */ @@ -36,25 +29,8 @@ @ConfigurationProperties(prefix = "reactivity.couchbase") public class CouchbaseConfig { - /** - * Default value for property {@code reactivity.couchbase.nodes}. - */ - public static final String DEFAULT_COUCHBASE_NODES = "127.0.0.1"; - - /** - * Default value for property {@code reactivity.couchbase.bucket}. - */ - public static final String DEFAULT_COUCHBASE_BUCKET = "artifact"; - - /** - * Nodes to use. See {@link #DEFAULT_COUCHBASE_NODES default} value. - */ - private String[] nodes = new String[] { DEFAULT_COUCHBASE_NODES } ; - - /** - * Bucket name. See {@link #DEFAULT_COUCHBASE_BUCKET default} value. - */ - private String bucket = DEFAULT_COUCHBASE_BUCKET; +// private String[] nodes = new String[] { "127.0.0.1" } ; + private String[] nodes = new String[] { "ec2-35-160-191-149.us-west-2.compute.amazonaws.com" } ; /** *

        @@ -63,10 +39,18 @@ public class CouchbaseConfig { * * @return the {@code Bucket} */ +// @Bean +// Bucket sync() { +// final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); +// final Bucket bucket = cluster.openBucket("artifact"); +// bucket.bucketManager().createN1qlPrimaryIndex(true, false); +// +// return bucket; +// } @Bean Bucket sync() { final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); - final Bucket bucket = cluster.openBucket(this.bucket); + final Bucket bucket = cluster.openBucket("default"); bucket.bucketManager().createN1qlPrimaryIndex(true, false); return bucket; @@ -82,15 +66,4 @@ Bucket sync() { public void setNodes(final String[] nodes) { this.nodes = nodes; } - - /** - *

        - * Sets the bucket where data are managed. - *

        - * - * @param bucket the bucket name - */ - public void setBucket(final String bucket) { - this.bucket = bucket; - } } diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java index 01f33e0..db51500 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java @@ -23,10 +23,7 @@ import io.reactivity.core.lib.event.Organization; import io.reactivity.core.lib.ReactivityEntity; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; /** @@ -52,7 +49,7 @@ public class EventController { *

        * * @param viewId the view ID - * @param limit maximum number of returned artifacts + * @param limit maximum number of returned artifacts * @param maxAge highest possible age for an artifact * @return the event flux */ diff --git a/core/broadcaster/src/main/resources/mocks/index.html b/core/broadcaster/src/main/resources/mocks/index.html index f2529b8..8933875 100644 --- a/core/broadcaster/src/main/resources/mocks/index.html +++ b/core/broadcaster/src/main/resources/mocks/index.html @@ -7,9 +7,9 @@

        Events: -

          - - +
            + +

            + diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html index a784c30..1d402a5 100644 --- a/front/src/components/app-orga.html +++ b/front/src/components/app-orga.html @@ -1,191 +1,117 @@ + + + + + + + + + + - - diff --git a/front/src/components/app-view.html b/front/src/components/app-view.html index 3e10e5c..5a6c0c1 100644 --- a/front/src/components/app-view.html +++ b/front/src/components/app-view.html @@ -1,10 +1,47 @@ - - diff --git a/front/src/constants/const.mixin.js b/front/src/constants/const.mixin.js new file mode 100644 index 0000000..eaba4dd --- /dev/null +++ b/front/src/constants/const.mixin.js @@ -0,0 +1,5 @@ +let GlobalConst = (superclass) => class extends superclass { + get wsURL(){ + return "http://localhost:3000/api"; + }; +}; \ No newline at end of file diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index d2ac8df..730e21e 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -3,59 +3,65 @@ - - - - + + + + + + diff --git a/front/src/shell/navbar/navbar.html b/front/src/shell/navbar/navbar.html index 464dbe3..5684509 100644 --- a/front/src/shell/navbar/navbar.html +++ b/front/src/shell/navbar/navbar.html @@ -18,7 +18,7 @@ padding: 0 64px; overflow: hidden; justify-content: space-between; - box-shadow: 0 1px 7px 0 black; + box-shadow: 0 5px 10px rgba(0,0,0,.15); } .horizontal-wrapper { diff --git a/front/src/shell/pages/organisation/organisation.html b/front/src/shell/pages/organisation/organisation.html index aca70c6..4e639c6 100644 --- a/front/src/shell/pages/organisation/organisation.html +++ b/front/src/shell/pages/organisation/organisation.html @@ -1,10 +1,28 @@ + - diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js index e0b97fd..57b5857 100644 --- a/front/src/shell/pages/organisation/organisation.js +++ b/front/src/shell/pages/organisation/organisation.js @@ -1,24 +1,24 @@ -class ShellOrganisation extends Polymer.Element { +class ShellOrganisation extends GlobalConst(Polymer.Element) { static get is() { return 's-organisation'; } - + constructor (props) { + super(props); + this.views = []; + this.artifacts = []; + } connectedCallback() { super.connectedCallback(); if(this.id) { var req = new XMLHttpRequest(); - req.open('GET', `http://localhost:3000/api/subscribe/${this.id}`, true); + req.open('GET', `${this.wsURL}/subscribe/${this.id}`, true); req.onreadystatechange = () => { if (req.readyState == 4) { if (req.status == 200) { this.data = JSON.parse(req.responseText); var groups = _.groupBy(this.data, "event"); - - - //TODO:DO something with that !!! {{info}} - - + this.setProperties({views : groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); } else { alert("Erreur pendant le chargement de la page.\n"); diff --git a/front/src/shell/pages/organisations/organisations.html b/front/src/shell/pages/organisations/organisations.html index 12acf9d..b0226c5 100644 --- a/front/src/shell/pages/organisations/organisations.html +++ b/front/src/shell/pages/organisations/organisations.html @@ -1,4 +1,4 @@ - + - - diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js index 4cd8b81..38e51db 100644 --- a/front/src/shell/pages/organisations/organisations.js +++ b/front/src/shell/pages/organisations/organisations.js @@ -1,4 +1,4 @@ -class ShellOrganisations extends Polymer.Element { +class ShellOrganisations extends GlobalConst(Polymer.Element) { static get is() { return 's-organisations'; } @@ -11,7 +11,7 @@ class ShellOrganisations extends Polymer.Element { connectedCallback() { super.connectedCallback(); var req = new XMLHttpRequest(); - req.open('GET', 'http://localhost:3000/api/load/organizations', true); + req.open('GET', `${this.wsURL}/load/organizations`, true); req.onreadystatechange = () => { if (req.readyState == 4) { if(req.status == 200) { diff --git a/front/src/shell/pages/view/view.html b/front/src/shell/pages/view/view.html new file mode 100644 index 0000000..78df1e3 --- /dev/null +++ b/front/src/shell/pages/view/view.html @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js new file mode 100644 index 0000000..4d87943 --- /dev/null +++ b/front/src/shell/pages/view/view.js @@ -0,0 +1,59 @@ +class ShellView extends GlobalConst(Polymer.Element) { + static get is() { + return 's-view'; + } + constructor (props) { + super(props); + this.limit = 50; + this.artifacts = []; + //load on init + this.isLoading = true; + this.maxage = -1; + } + connectedCallback() { + super.connectedCallback(); + + //throttle : function that only invokes func at most once per every wait milliseconds (350) + this._scroll= _.throttle(this.scroll.bind(this), 350); + this.$.content.addEventListener('scroll', this._scroll); + + if(this.id) { + this.fetch(this.limit, -1, (data) => { + this.setProperties({artifacts: data}); + }) + } + } + scroll(e) { + if(this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { + if (!this.isLoading && this.maxage ) { + this.fetch(100, this.maxage, (data) => { + this.setProperties({artifacts: this.artifacts.concat(data)}); + }) + } + } + } + fetch(limit, maxage, callback) { + var data; + var req = new XMLHttpRequest(); + this.isLoading = true; + req.open('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`, true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if (req.status == 200) { + data = JSON.parse(req.responseText); + // -1 to be sure to not get the last item infinitely + if (data.length) { + this.maxage = data[data.length - 1].updated - 1; + callback(data); + } + } else { + alert("Erreur pendant le chargement de la page.\n"); + } + this.isLoading = false; + } + }; + req.send(null); + } +} +// Register custom element definition using standard platform API +customElements.define(ShellView.is, ShellView); \ No newline at end of file diff --git a/front/src/utils/eventSource.util.js b/front/src/utils/eventSource.util.js deleted file mode 100644 index c7d82e0..0000000 --- a/front/src/utils/eventSource.util.js +++ /dev/null @@ -1,77 +0,0 @@ -window.EventSourceManager = class EventSourceManager { - constructor(url, manager) { - //Create an Observable for the EventSource Connection - this.sourceObservable = Rx.Observable.create((observer) => { - this.source = new EventSource(url); - - const onOpen = (e) => { - observer.onNext(e); - console.log("%c SSE reconnected", "font-size:22px;text-shadow: 0 0 3px #FF0000, 0 0 5px blue;"); - - // this.source.removeEventListener('open', onOpen, false); - }; - - //TODO : On error subscribe to a new stream (wso ) - const onError = (e) => { - if (e.eventPhase === EventSource.CLOSED) { - observer.onCompleted(); - } else { - observer.onError(e); - } - }; - - const onMessage = (e) => { - observer.onNext(e); - }; - - this.source.addEventListener('open', onOpen, false); - this.source.addEventListener('error', onError, false); - this.source.addEventListener('message', onMessage, false); - - //Add listener on specific eventType - this.addEventsObservables(manager); - - return () => { - this.source.removeEventListener('error', onError, false); - this.source.removeEventListener('message', onMessage, false); - this.source.close(); - }; - }); - } - - static get source() { - return this.source; - } - - static get sourceObservable() { - return this.sourceObservable; - } - - /** - * Create an observable for each property of the given manager - * @param {Object} manager - * @returns {void} - */ - addEventsObservables(manager) { - //for each property - for (let eventType in manager) { - if (manager.hasOwnProperty(eventType)) { - - //Create a new observable - const observable = Rx.Observable.create((observer) => { - const onMessage = (e) => { - observer.onNext(e); - }; - - //listen the eventType on the EventSource - this.source.addEventListener(eventType, onMessage, false); - }); - - //Subscribe the callback. - observable.subscribe((e) => { - manager[eventType](e); - }); - } - } - } -}; \ No newline at end of file From 59d19d5c5a5279b59746425214e7446a198f593a Mon Sep 17 00:00:00 2001 From: nathandm Date: Mon, 9 Jan 2017 17:31:17 +0100 Subject: [PATCH 019/132] front: infinit scroll loader --- front/src/components/app-artifact-list.html | 76 +++++++++++++++++++-- front/src/shell/navbar/navbar.js | 8 +-- front/src/shell/pages/view/view.html | 8 ++- front/src/shell/pages/view/view.js | 13 ++-- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/front/src/components/app-artifact-list.html b/front/src/components/app-artifact-list.html index 83de92e..1034efb 100644 --- a/front/src/components/app-artifact-list.html +++ b/front/src/components/app-artifact-list.html @@ -29,11 +29,65 @@ color: #555555; margin:0 5px 0 0; } + + /*Loader*/ + .loader{ + list-style-type: none; + float: left; + } + .loader li { + display: inline-block; + height: .5em; + width: .5em; + margin-bottom: .7em; + background: white; + border-radius: 50%; + transition: opacity 0.22222s ease; + animation: bbbounce 2s infinite ease; + } + .loader li:nth-of-type(1){ + animation-delay: 0.22222s; + } + .loader li:nth-of-type(2) { + animation-delay: 0.44444s; + } + .loader li:nth-of-type(3) { + animation-delay: 0.66667s; + } + + @keyframes bbbounce { + 0%,100% { + opacity: 1; + transform: translateY(0em) scale(1); + } + 50% { + opacity: .8; + transform: translateY(1em) scale(0.8); + } + } + -
            -

            {{description}}

            -

            {{assignee}},

            {{priority}},

            {{category}}

            -
            + + + + + + + + diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js index 4d87943..950a136 100644 --- a/front/src/shell/pages/view/view.js +++ b/front/src/shell/pages/view/view.js @@ -14,20 +14,21 @@ class ShellView extends GlobalConst(Polymer.Element) { super.connectedCallback(); //throttle : function that only invokes func at most once per every wait milliseconds (350) - this._scroll= _.throttle(this.scroll.bind(this), 350); - this.$.content.addEventListener('scroll', this._scroll); + this._scrollListener= _.throttle(this._handleScroll.bind(this), 350); + this.$.content.addEventListener('scroll', this._scrollListener); if(this.id) { this.fetch(this.limit, -1, (data) => { - this.setProperties({artifacts: data}); + this.setProperties({artifacts: data, isLoading: false}); }) } } - scroll(e) { + _handleScroll() { if(this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { if (!this.isLoading && this.maxage ) { + this.setProperties({isLoading : true}); this.fetch(100, this.maxage, (data) => { - this.setProperties({artifacts: this.artifacts.concat(data)}); + this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); }) } } @@ -35,7 +36,6 @@ class ShellView extends GlobalConst(Polymer.Element) { fetch(limit, maxage, callback) { var data; var req = new XMLHttpRequest(); - this.isLoading = true; req.open('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`, true); req.onreadystatechange = () => { if (req.readyState == 4) { @@ -49,7 +49,6 @@ class ShellView extends GlobalConst(Polymer.Element) { } else { alert("Erreur pendant le chargement de la page.\n"); } - this.isLoading = false; } }; req.send(null); From 0fb7c2fd8e5b587b0950835dbbe8c37e552f6f2f Mon Sep 17 00:00:00 2001 From: nathandm Date: Tue, 10 Jan 2017 14:05:22 +0100 Subject: [PATCH 020/132] front: create MixinBuilder + http.mixin + globalConst.mixin - manage subclass with mixin - create a http(XMLHttpRequest) mixin - externalise global constant un a dedicated mixin Signed-off-by: nathandm --- front/.eslintrc | 16 +++--- front/bower.json | 3 +- front/package.json | 2 + front/src/components/app-artifact-list.html | 21 ++++--- .../src/{constants => mixins}/const.mixin.js | 6 +- front/src/mixins/http.mixin.js | 27 +++++++++ front/src/mixins/mixinBuilder.js | 18 ++++++ front/src/reactivity-shell.html | 37 ++++++++++--- front/src/shell/navbar/navbar.js | 6 +- .../shell/pages/organisation/organisation.js | 29 ++++------ .../pages/organisations/organisations.js | 20 ++----- front/src/shell/pages/settings/settings.js | 2 +- front/src/shell/pages/view/view.js | 55 ++++++++----------- 13 files changed, 144 insertions(+), 98 deletions(-) rename front/src/{constants => mixins}/const.mixin.js (51%) create mode 100644 front/src/mixins/http.mixin.js create mode 100644 front/src/mixins/mixinBuilder.js diff --git a/front/.eslintrc b/front/.eslintrc index 1cc8ff1..e74ecc6 100644 --- a/front/.eslintrc +++ b/front/.eslintrc @@ -6,22 +6,22 @@ "node": true, "es6": true }, - "extends": "airbnb", // supposedly good, ajusting some values below. + "extends": "google", // supposedly good, ajusting some values below. "rules": { "arrow-body-style": ["error", "as-needed"], - "jsx-closing-bracket-location": 0, // doesn't always improve readability - "react/jsx-space-before-closing" : 0, - "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], "comma-dangle": 0, // too much "no-console": 0, "spaced-comment": 0, // sucks for code comments - "max-len": ["error", 160], // I don't code on my smartphone, need more than the default 80 - "no-param-reassign": ["error", { "props": false }] + "max-len": ["error", 120], // I don't code on my smartphone, need more than the default 80 + "no-param-reassign": ["error", { "props": false }], + "require-jsdoc": 0, + "new-cap": 0,//Mixin don't need new + "no-unused-vars": 0 //Mixin have global def }, "settings": { "import/resolve": { - "extensions": [ ".js", ".jsx" ] + "extensions": [ ".js" ] }, "import/parser": "babel-eslint" } -} \ No newline at end of file +} diff --git a/front/bower.json b/front/bower.json index 988c576..7a2824a 100644 --- a/front/bower.json +++ b/front/bower.json @@ -14,7 +14,8 @@ "paper-checkbox": "2.0-preview", "paper-icon-button": "2.0-preview", "app-router": "blasten/app-router#master", - "lodash": "^4.17.4" + "lodash": "^4.17.4", + "q": "^1.4.1" }, "resolutions": { "polymer": "2.0-preview", diff --git a/front/package.json b/front/package.json index b872599..8d1ec30 100644 --- a/front/package.json +++ b/front/package.json @@ -13,6 +13,8 @@ "bower": "^1.8.0", "browser-sync": "^2.18.5", "connect-history-api-fallback": "^1.3.0", + "eslint": "^3.13.1", + "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", "http-proxy-middleware": "^0.17.3" } diff --git a/front/src/components/app-artifact-list.html b/front/src/components/app-artifact-list.html index 1034efb..f650d23 100644 --- a/front/src/components/app-artifact-list.html +++ b/front/src/components/app-artifact-list.html @@ -16,25 +16,29 @@ padding: 0 10px; box-sizing: border-box; } + .description { font-size: 16px; color: white; - margin:0; + margin: 0; } + .sub { display: flex; } + .sub p { font-size: 14px; color: #555555; - margin:0 5px 0 0; + margin: 0 5px 0 0; } /*Loader*/ - .loader{ + .loader { list-style-type: none; float: left; } + .loader li { display: inline-block; height: .5em; @@ -45,18 +49,21 @@ transition: opacity 0.22222s ease; animation: bbbounce 2s infinite ease; } - .loader li:nth-of-type(1){ + + .loader li:nth-of-type(1) { animation-delay: 0.22222s; } + .loader li:nth-of-type(2) { animation-delay: 0.44444s; } + .loader li:nth-of-type(3) { animation-delay: 0.66667s; } @keyframes bbbounce { - 0%,100% { + 0%, 100% { opacity: 1; transform: translateY(0em) scale(1); } @@ -87,7 +94,6 @@ - - + + + +
            diff --git a/front/src/shell/navbar/navbar.js b/front/src/shell/navbar/navbar.js index 365aadb..7e2226b 100644 --- a/front/src/shell/navbar/navbar.js +++ b/front/src/shell/navbar/navbar.js @@ -3,11 +3,11 @@ class ShellNavbar extends Polymer.Element { return 's-navbar'; } - handleClick (e) { + handleClick(e) { e.preventDefault(); - document.dispatchEvent(new CustomEvent('nav',{detail: {path:e.currentTarget.pathname}})); + document.dispatchEvent(new CustomEvent('nav', {detail: {path: e.currentTarget.pathname}})); } } // Register custom element definition using standard platform API -customElements.define(ShellNavbar.is, ShellNavbar); \ No newline at end of file +customElements.define(ShellNavbar.is, ShellNavbar); diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js index 57b5857..b39f67b 100644 --- a/front/src/shell/pages/organisation/organisation.js +++ b/front/src/shell/pages/organisation/organisation.js @@ -1,33 +1,24 @@ -class ShellOrganisation extends GlobalConst(Polymer.Element) { +class ShellOrganisation extends mix(Polymer.Element).with(GlobalConst, Http) { static get is() { return 's-organisation'; } - constructor (props) { + + constructor(props) { super(props); this.views = []; this.artifacts = []; } + connectedCallback() { super.connectedCallback(); - if(this.id) { - var req = new XMLHttpRequest(); - req.open('GET', `${this.wsURL}/subscribe/${this.id}`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if (req.status == 200) { - this.data = JSON.parse(req.responseText); - var groups = _.groupBy(this.data, "event"); - this.setProperties({views : groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); - - } else { - alert("Erreur pendant le chargement de la page.\n"); - } - } - }; - req.send(null); + if (this.id) { + this.fetch('GET', `${this.wsURL}/subscribe/${this.id}`).then((data) => { + const groups = _.groupBy(data, 'event'); + this.setProperties({views: groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); + }); } } } // Register custom element definition using standard platform API -customElements.define(ShellOrganisation.is, ShellOrganisation); \ No newline at end of file +customElements.define(ShellOrganisation.is, ShellOrganisation); diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js index 38e51db..71e3f37 100644 --- a/front/src/shell/pages/organisations/organisations.js +++ b/front/src/shell/pages/organisations/organisations.js @@ -1,4 +1,4 @@ -class ShellOrganisations extends GlobalConst(Polymer.Element) { +class ShellOrganisations extends mix(Polymer.Element).with(GlobalConst, Http) { static get is() { return 's-organisations'; } @@ -10,19 +10,11 @@ class ShellOrganisations extends GlobalConst(Polymer.Element) { connectedCallback() { super.connectedCallback(); - var req = new XMLHttpRequest(); - req.open('GET', `${this.wsURL}/load/organizations`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if(req.status == 200) { - this.organisations = JSON.parse(req.responseText); - } else { - alert("Don't be silly launch the server !\n"); - } - } - }; - req.send(null); + + this.fetch('GET', `${this.wsURL}/load/organizations`).then((data) => { + this.organisations = data; + }); } } // Register custom element definition using standard platform API -customElements.define(ShellOrganisations.is, ShellOrganisations); \ No newline at end of file +customElements.define(ShellOrganisations.is, ShellOrganisations); diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js index b60a0aa..2db4e23 100644 --- a/front/src/shell/pages/settings/settings.js +++ b/front/src/shell/pages/settings/settings.js @@ -3,4 +3,4 @@ class ShellSettings extends Polymer.Element { return 's-settings'; } } -customElements.define(ShellSettings.is, ShellSettings); \ No newline at end of file +customElements.define(ShellSettings.is, ShellSettings); diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js index 950a136..66250f1 100644 --- a/front/src/shell/pages/view/view.js +++ b/front/src/shell/pages/view/view.js @@ -1,8 +1,9 @@ -class ShellView extends GlobalConst(Polymer.Element) { +class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { static get is() { return 's-view'; } - constructor (props) { + + constructor(props) { super(props); this.limit = 50; this.artifacts = []; @@ -10,49 +11,37 @@ class ShellView extends GlobalConst(Polymer.Element) { this.isLoading = true; this.maxage = -1; } + connectedCallback() { super.connectedCallback(); - //throttle : function that only invokes func at most once per every wait milliseconds (350) - this._scrollListener= _.throttle(this._handleScroll.bind(this), 350); + //throttle:function that only invokes func at most once every (350) milli sec + this._scrollListener = _.throttle(this._handleScroll.bind(this), 350); this.$.content.addEventListener('scroll', this._scrollListener); - if(this.id) { - this.fetch(this.limit, -1, (data) => { - this.setProperties({artifacts: data, isLoading: false}); - }) + if (this.id) { + this.fetchNextData(this.limit, -1); } } + _handleScroll() { - if(this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { - if (!this.isLoading && this.maxage ) { - this.setProperties({isLoading : true}); - this.fetch(100, this.maxage, (data) => { - this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); - }) + if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { + if (!this.isLoading && this.maxage) { + this.setProperties({isLoading: true}); + this.fetchNextData(100, this.maxage); } } } - fetch(limit, maxage, callback) { - var data; - var req = new XMLHttpRequest(); - req.open('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if (req.status == 200) { - data = JSON.parse(req.responseText); - // -1 to be sure to not get the last item infinitely - if (data.length) { - this.maxage = data[data.length - 1].updated - 1; - callback(data); - } - } else { - alert("Erreur pendant le chargement de la page.\n"); + + fetchNextData(limit, maxage) { + this.fetch('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`) + .then((data) => { + if (data.length) { + this.maxage = data[data.length - 1].updated - 1; + this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); } - } - }; - req.send(null); + }); } } // Register custom element definition using standard platform API -customElements.define(ShellView.is, ShellView); \ No newline at end of file +customElements.define(ShellView.is, ShellView); From 4363b8cf4fbde566b8b6ffd75b409b0bb17ba5f3 Mon Sep 17 00:00:00 2001 From: nathandm Date: Tue, 10 Jan 2017 15:34:17 +0100 Subject: [PATCH 021/132] front: navBar replace logo with previous history --- front/src/reactivity-shell.html | 9 --------- front/src/shell/navbar/idea.png | Bin 112692 -> 0 bytes front/src/shell/navbar/navbar.html | 23 +++++++++-------------- front/src/shell/navbar/navbar.js | 4 ++++ 4 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 front/src/shell/navbar/idea.png diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index adf9e05..118714b 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -50,15 +50,6 @@ return 's-reactivity'; } - static get config() { - return { - listeners: { - 'nav': '_onNav', - 'error': '_onError' - } - } - } - connectedCallback() { super.connectedCallback(); diff --git a/front/src/shell/navbar/idea.png b/front/src/shell/navbar/idea.png deleted file mode 100644 index e65b6587696a80aa728b2cb0a7b37cbb46ea2c50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112692 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%0A+70|6Jra*EdGmbSDu= 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�-|)VK`U%Fow=|W)Ua3@WH4Pw=&a} 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 + - - diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index 118714b..13c5d25 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -63,6 +63,11 @@ document.addEventListener('error', () => { this.$.router.go('/404'); }); + + document.addEventListener('changeColor', (e) => { + this.updateStyles({'--app-primary-color': e.detail.primary}); + this.updateStyles({'--app-secondary-color': e.detail.secondary}); + }); //No need to removeListener cause this is the main entry point of the application (if unmount no more fun) } } diff --git a/front/src/shell/pages/404/view-404.html b/front/src/shell/pages/404/view-404.html index 8cab5bc..5aea619 100644 --- a/front/src/shell/pages/404/view-404.html +++ b/front/src/shell/pages/404/view-404.html @@ -3,6 +3,11 @@ diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js index 2db4e23..4940fcc 100644 --- a/front/src/shell/pages/settings/settings.js +++ b/front/src/shell/pages/settings/settings.js @@ -2,5 +2,14 @@ class ShellSettings extends Polymer.Element { static get is() { return 's-settings'; } + + changeColor(e) { + document.dispatchEvent(new CustomEvent('changeColor', { + detail: { + primary: e.currentTarget.getAttribute('primary'), + secondary: e.currentTarget.getAttribute('secondary') + } + })); + } } customElements.define(ShellSettings.is, ShellSettings); diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js index 66250f1..ce15bc2 100644 --- a/front/src/shell/pages/view/view.js +++ b/front/src/shell/pages/view/view.js @@ -5,7 +5,9 @@ class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { constructor(props) { super(props); - this.limit = 50; + this.artifactLimit = 50; + //Limit in pixel before fetching others data + this.threshold = 1500; this.artifacts = []; //load on init this.isLoading = true; @@ -14,27 +16,23 @@ class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { connectedCallback() { super.connectedCallback(); - - //throttle:function that only invokes func at most once every (350) milli sec - this._scrollListener = _.throttle(this._handleScroll.bind(this), 350); - this.$.content.addEventListener('scroll', this._scrollListener); - if (this.id) { - this.fetchNextData(this.limit, -1); + this.$.content.addEventListener('scroll', this._handleScroll.bind(this)); + this.fetchNextData(this.artifactLimit, -1); } } _handleScroll() { - if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { + if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - this.threshold) { if (!this.isLoading && this.maxage) { this.setProperties({isLoading: true}); - this.fetchNextData(100, this.maxage); + this.fetchNextData(this.artifactLimit, this.maxage); } } } fetchNextData(limit, maxage) { - this.fetch('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`) + this.fetch('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${limit}/maxage/${maxage}`) .then((data) => { if (data.length) { this.maxage = data[data.length - 1].updated - 1; From cc2aa1e5e35a327c188916c781510b94f9315201 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 11:54:51 +0100 Subject: [PATCH 023/132] test: front test with travis --- .travis.yml | 35 ++++++++++++++++++++++++++++------- front/package.json | 3 ++- front/test/routertest.html | 19 +++++++++++++++++++ wct.conf.json | 18 ++++++++++++++++++ 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 front/test/routertest.html create mode 100644 wct.conf.json diff --git a/.travis.yml b/.travis.yml index 726734b..3a126d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,40 @@ language: java jdk: - - oraclejdk8 +- oraclejdk8 +dist: trusty +sudo: required +addons: + firefox: latest + sauce_connect: true + apt: + sources: + - google-chrome + packages: + - google-chrome-stable env: + matrix: - MODULE=core - MODULE=core/java-lib - MODULE=core/broadcaster + global: + - secure: jyieF62Bdxn8KzePW73qAWOsV6n83gIGDpKGpgbpbCDG+R5AcMqFSXf32A7So148YczZIjlhWUx85Vrol9NURpimxcFfZU9nKCPjZt7QpGuzXInbxy8Qrze1I/PN40nMVGxNEam7pwXIMZwbOFPv/PG+fDw7BCDSfsoTwTMbk1FvjhpBngo6DBtbyld30HEhIVSSLhROgcQ7zzcfHYdu9IxvnDFnuNict1m1+HGJf0jDAbFRzGteMq7/tu+rM9HHd5UyCSI2sGWo4P3tAcXw/l3VV/MxhG6cRofvQQEY7H+2JpJ5U6CDprrN7sOUrc777cmOzXzB1sYLaNNniQiScQtZJTYcJbhPF/rOYn8FjrFDMxC0EB7oQQ58Z4dUViKNAdR54zxFwm/OqDssAV43fAOFfkiZGfdKjR+CQQMWRD7UdaCfsy3w/xFw5KiNObyUtvbBJABVYWjsIIF0yU20Qu8Cr5DCfUjUTyNiWz/LjN3cQjtv6HB7A3UWc/ohjhU/GZoWvvJ5WySKLMuL8msH5/R3UE+u8TZYTi9pSG/k6BSoVO4DRAmvG/7FdepwHJYnW1Ol1Bd2u0OdqiwhByu0yDH9jolJ/VYBq8A880nU+dW5SVhwTNSvLgo3jclr4IwkCydZvZ6YjiqpKSqW69TRWyPLOFVLtn943yFX89OXhwY= + - secure: Y0Ck+5ZLdbv2qC5uySGZZmuU1++fBvLOtUquAiFn4IPXzaMTbURqr0jY6+R/ibiJdnTgKk4H65Gl4S2d2UPSVTeIUoxnzSSiUHhoxp1FUrGuK8wxuk0ZMffRIMWODRxLJsuFKXTt7NpwGmDx53Kzzp0cbMo9ilkVFJu+6PBxmTC2lRZwIeHFo4U56DjNwWaHA3dXwfiv1S6tsfem3kxGHb9SsfN4UGyXTRqKZBK+DwEs07Uwwq6FkCqsSzcV42uTx2t9qRrVbs3elIPdNKIitEE/V+juQTg94c3BHspXHZhgExByAm/2SSOZaE2syyrFGPSPRf1XKbzwkiGCJiCWzT8naOZB8CC6V4QxDwbrh6nU9lH2BQdcDZj9/pcT6HPc8nhoBKwl4Rzpnsva4b70cZE57KaRwiDQ4lUvFPJ3YepPiz0QKNnEPYHAbLFIMBvsVcMGGkKg4aMSiBfCXu62PTAAqUNQJKRVEIpYAs6AHE3IUUsh+gZ24yWddEHRE3mUvi3vIYnNUjlt0e9fTqfitl1wMV68CZb5llm19jLYFZ0uX54d0xkCtdmNWBvZBip4lVpuR/qQYgLp2LOiEqgzFNLg4x6DxRabUAZ0tP/daIQ2WBBTXKIkxCk2Am/xwFCyz9Bzb97DTSUjvdtI++bGd1nn6LN8AZ01RhO4I5Rff/0= + before_install: - - cp config/settings.xml ~/.m2/settings.xml - - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc -script: - - mvn -f $MODULE/pom.xml clean deploy +- cp config/settings.xml ~/.m2/settings.xml +- echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc +before_script: +- npm install -g bower polylint web-component-tester +- bower install +- polylint +script: +- mvn -f $MODULE/pom.xml clean deploy +- xvfb-run wct front/test --skip-plugin sauce +- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct front/test -s 'default' --skip-plugin local; fi deploy: - provider: cloudfoundry api: https://api.run.pivotal.io - username: $CF_DEPLOY_USERNAME + username: "$CF_DEPLOY_USERNAME" password: secure: RE8snuhO5HKpV8RUPpVXP7KTaqSJb3DS4FSIzaCMy/+N54Uqvnk3ecFyXGJpgYSCRmiZEohP9hXSGYaRX2aXOQvnxtnRcH73X0J8c5T1o3gvLKUdlyZg2HIEMUu7x2pVekJqe300hlhi5Uxgm2MLjdFD74xhoN7USKnBdXOF/cyjyIomEr4HX8UCxMxjbgeAyGXvPyrH1bQbp6j9QdZy06zpMD0+MYzl66qYV4pgxeey/jyqh+ldAQOtXqXueIGG3PQr+zFQTX9Jnw7ilKvP9HqTAngt32GaccB+zCmMhmKFouodJzKtFnpGOQ6d82XgdKMcZcZfGK9zMI1ZDVLWrftfjap+8TFuHpiJbghj52bGrMAD0/+d14LxsSVj0NyuQ7a/7RwalfF9aZOs1CNrLO9KkKy+5ncUKv6dLZtBZ9bN9+bwdb3pfS78C7JaJQOkv6jjXYIJv/yssBla90gfXJY+htnax+5Owss391JYsjhKFtbYL9LhEe587t5lsz7tkOygRPQ+zARInmJt6DBc/rHdtLDMOdhEaA+MKt9Sfn2TZuTXKsu3/pbM95+061u3wmZl7jxqjhnfMhHn1JkD0CkmWxbKLbL9ELy99TvySjdaT+qVar5AXSpL3iyLX/APjFz0RA+1jO8kOlfYebFZYNwoClJG7oTw9XcUuePGb7Q= organization: Reactivity @@ -22,4 +43,4 @@ deploy: on: repo: reactivity-io/reactivity branch: travis-deploy - condition: $MODULE = 'core/broadcaster' + condition: "$MODULE = 'core/broadcaster'" \ No newline at end of file diff --git a/front/package.json b/front/package.json index 8d1ec30..eabf110 100644 --- a/front/package.json +++ b/front/package.json @@ -16,6 +16,7 @@ "eslint": "^3.13.1", "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", - "http-proxy-middleware": "^0.17.3" + "http-proxy-middleware": "^0.17.3", + "web-component-tester": "^5.0.0" } } diff --git a/front/test/routertest.html b/front/test/routertest.html new file mode 100644 index 0000000..e3b090d --- /dev/null +++ b/front/test/routertest.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/wct.conf.json b/wct.conf.json new file mode 100644 index 0000000..ea4cbec --- /dev/null +++ b/wct.conf.json @@ -0,0 +1,18 @@ +{ + "verbose": false, + "plugins": { + "sauce": { + "disabled": true, + "browsers": [ + { + "browserName": "chrome", + "platform": "Windows 10", + "version": "55" + } + ] + }, + "local": { + "browsers": ["chrome"] + } + } +} \ No newline at end of file From 1161e49e613f92d209a6cf87fe60ae20df7ee6db Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 13:58:18 +0100 Subject: [PATCH 024/132] travis: launch test with travis Signed-off-by: nathandm --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3a126d5..3ed3725 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,9 +24,11 @@ before_install: - cp config/settings.xml ~/.m2/settings.xml - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc before_script: +- cd front - npm install -g bower polylint web-component-tester - bower install - polylint +- cd .. script: - mvn -f $MODULE/pom.xml clean deploy - xvfb-run wct front/test --skip-plugin sauce From a3ed62a99b797ce3e165882e1fab9bc1318250de Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 13:58:57 +0100 Subject: [PATCH 025/132] front: http mixin + change couchbase db --- .../broadcaster/config/CouchbaseConfig.java | 2 +- .../core/broadcaster/RepositoryTest.java | 2 +- front/gulpfile.js | 1 - front/src/mixins/http.mixin.js | 24 ++++++++++++++++--- front/src/reactivity-shell.html | 3 +-- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 6465318..9ecc9bf 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -50,7 +50,7 @@ public class CouchbaseConfig { @Bean Bucket sync() { final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); - final Bucket bucket = cluster.openBucket("default"); + final Bucket bucket = cluster.openBucket("nana"); bucket.bucketManager().createN1qlPrimaryIndex(true, false); return bucket; diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java index 6809d32..f1f0e7a 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -81,6 +81,6 @@ public void insertSomeArtifactsDocuments() { .put("categories", JsonObject.from(categories)); return bucket.insert(JsonDocument.create(id, object)); - }).repeat(1000).subscribe(); + }).repeat(100).subscribe(); } } diff --git a/front/gulpfile.js b/front/gulpfile.js index b153f6a..12128a6 100644 --- a/front/gulpfile.js +++ b/front/gulpfile.js @@ -25,4 +25,3 @@ gulp.task('default', () => { } }); }); - diff --git a/front/src/mixins/http.mixin.js b/front/src/mixins/http.mixin.js index c2c1974..d153108 100644 --- a/front/src/mixins/http.mixin.js +++ b/front/src/mixins/http.mixin.js @@ -12,11 +12,11 @@ let Http = (superclass) => class extends superclass { deferred.resolve(retval); } catch (e) { deferred.reject(e.message); - document.dispatchEvent(new CustomEvent('error', {detail: {message: e.message}})); + this.dispatchCustomError(e.message); } } else { - deferred.reject(req.error); - document.dispatchEvent(new CustomEvent('error')); + deferred.reject(req.responseText); + this.dispatchRequestError(req); } } }; @@ -24,4 +24,22 @@ let Http = (superclass) => class extends superclass { return deferred.promise; } + + dispatchRequestError(req) { + document.dispatchEvent(new CustomEvent('error', { + detail: { + status: req.status, + statusText: req.statusText, + message: req.responseText + } + })); + } + + dispatchCustomError(message) { + document.dispatchEvent(new CustomEvent('error', { + detail: { + message: message + } + })); + } }; diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index 13c5d25..0029f2b 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -28,7 +28,7 @@ } - + - - + + From 7d989b4284a523362b796f3d29ef40807a1258db Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 15:56:37 +0100 Subject: [PATCH 033/132] travis: change dep web-component-tester from bower --- front/bower.json | 4 ++++ front/package.json | 3 +-- front/test/routertest.html | 3 +-- wct.conf.json | 7 +------ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/front/bower.json b/front/bower.json index 7a2824a..c42299f 100644 --- a/front/bower.json +++ b/front/bower.json @@ -17,7 +17,11 @@ "lodash": "^4.17.4", "q": "^1.4.1" }, + "devDependencies": { + "web-component-tester": "^4.0.0" + }, "resolutions": { + "lodash": "^3.7.0", "polymer": "2.0-preview", "webcomponentsjs": "v1" } diff --git a/front/package.json b/front/package.json index eabf110..8d1ec30 100644 --- a/front/package.json +++ b/front/package.json @@ -16,7 +16,6 @@ "eslint": "^3.13.1", "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", - "http-proxy-middleware": "^0.17.3", - "web-component-tester": "^5.0.0" + "http-proxy-middleware": "^0.17.3" } } diff --git a/front/test/routertest.html b/front/test/routertest.html index 962b71a..c67bd33 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -2,8 +2,7 @@ - - + diff --git a/wct.conf.json b/wct.conf.json index aba3bcd..3f5054c 100644 --- a/wct.conf.json +++ b/wct.conf.json @@ -5,14 +5,9 @@ "disabled": true, "browsers": [ { - "browserName": "microsoftedge", + "browserName": "chrome", "platform": "Windows 10", "version": "" - }, - { - "browserName": "internet explorer", - "platform": "Windows 8.1", - "version": "11" } ] }, From 607636d9d5e309b9749487302a634bebc0acf195 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 16:15:07 +0100 Subject: [PATCH 034/132] travis: more test :) --- front/test/awesome-tests.js | 8 ++++++++ front/test/routertest.html | 12 +++++------- 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 front/test/awesome-tests.js diff --git a/front/test/awesome-tests.js b/front/test/awesome-tests.js new file mode 100644 index 0000000..ed1aff9 --- /dev/null +++ b/front/test/awesome-tests.js @@ -0,0 +1,8 @@ +suite('', function () { + test('SHOULD BE TRUUUUe', function () { + assert.isNotNull({toto: 'toto'}); + }); + test('have navbar', function () { + assert.isNotNull(document.querySelector('s-reactivity').shadowRoot.querySelector('s-navbar')); + }); +}); \ No newline at end of file diff --git a/front/test/routertest.html b/front/test/routertest.html index c67bd33..716e933 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -2,17 +2,15 @@ - - + \ No newline at end of file From 70a88ea8644c649bc9845f3fedc4a7e36d8c5168 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 17:24:35 +0100 Subject: [PATCH 035/132] travis: fix on local test on travis --- .travis.yml | 2 +- front/package.json | 3 ++- front/test/awesome-tests.js | 8 -------- front/test/routertest.html | 17 +++++++++++------ 4 files changed, 14 insertions(+), 16 deletions(-) delete mode 100644 front/test/awesome-tests.js diff --git a/.travis.yml b/.travis.yml index 0ff7817..9c7a0d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ before_script: - cd .. script: - mvn -f $MODULE/pom.xml clean deploy -- xvfb-run wct front/ --plugin sauce --skip-plugin local +- xvfb-run wct front/test --plugin sauce --skip-plugin local deploy: - provider: cloudfoundry api: https://api.run.pivotal.io diff --git a/front/package.json b/front/package.json index 8d1ec30..142ffa1 100644 --- a/front/package.json +++ b/front/package.json @@ -16,6 +16,7 @@ "eslint": "^3.13.1", "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", - "http-proxy-middleware": "^0.17.3" + "http-proxy-middleware": "^0.17.3", + "webdriver-manager": "^11.1.1" } } diff --git a/front/test/awesome-tests.js b/front/test/awesome-tests.js deleted file mode 100644 index ed1aff9..0000000 --- a/front/test/awesome-tests.js +++ /dev/null @@ -1,8 +0,0 @@ -suite('', function () { - test('SHOULD BE TRUUUUe', function () { - assert.isNotNull({toto: 'toto'}); - }); - test('have navbar', function () { - assert.isNotNull(document.querySelector('s-reactivity').shadowRoot.querySelector('s-navbar')); - }); -}); \ No newline at end of file diff --git a/front/test/routertest.html b/front/test/routertest.html index 716e933..eadba9e 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -2,15 +2,20 @@ - + + - + \ No newline at end of file From 2b3b3a15356f52b1da397b70d83a834887c1ed86 Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 09:57:11 +0100 Subject: [PATCH 036/132] travis: add travis icon on readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c16275b..7c888d5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Reactivity +## Reactivity [![Build Status](https://travis-ci.org/reactivity-io/reactivity.svg?branch=master)](https://travis-ci.org/reactivity-io/reactivity) Reactivity is an open-source WEB application offering a visual way to manage your activity in a reactive manner. From 8c7b458fe0115b90a4dadde81421f8232cd569fa Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 10:33:08 +0100 Subject: [PATCH 037/132] travis: slack integration --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9c7a0d1..ee05c33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,4 +43,6 @@ deploy: on: repo: reactivity-io/reactivity branch: travis-deploy - condition: "$MODULE = 'core/broadcaster'" \ No newline at end of file + condition: "$MODULE = 'core/broadcaster'" +notifications: + slack: reactivity:eHHORgWgihkLbNPLR4B7RAOv \ No newline at end of file From d3ffc70fd40283199a5f66f162a52ce49db24833 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 6 Jan 2017 15:23:10 +0100 Subject: [PATCH 038/132] front: init structure of the apps :) --- front/.eslintrc | 27 + front/.gitignore | 4 + front/README.md | 19 + front/bower.json | 22 + front/gulpfile.js | 28 + front/index.html | 44 + front/manifest.json | 10 + front/package.json | 22 + front/src/components/app-card.html | 1440 +++++++++++++++++ front/src/components/app-orga.html | 226 +++ front/src/components/app-orga2.html | 143 ++ front/src/components/app-view.html | 24 + front/src/reactivity-shell.html | 61 + front/src/shell/navbar/idea.png | Bin 0 -> 112692 bytes front/src/shell/navbar/navbar.html | 68 + front/src/shell/navbar/navbar.js | 13 + front/src/shell/pages/404/polymerosaurus.png | Bin 0 -> 8469 bytes front/src/shell/pages/404/view-404.html | 77 + .../pages/organisation/organisation.html | 10 + .../shell/pages/organisation/organisation.js | 33 + .../pages/organisations/organisations.html | 43 + .../pages/organisations/organisations.js | 28 + front/src/shell/pages/settings/settings.html | 15 + front/src/shell/pages/settings/settings.js | 6 + front/src/shell/pages/views-wrapper.html | 96 ++ front/src/utils/eventSource.util.js | 77 + 26 files changed, 2536 insertions(+) create mode 100644 front/.eslintrc create mode 100644 front/.gitignore create mode 100644 front/README.md create mode 100644 front/bower.json create mode 100644 front/gulpfile.js create mode 100644 front/index.html create mode 100644 front/manifest.json create mode 100644 front/package.json create mode 100644 front/src/components/app-card.html create mode 100644 front/src/components/app-orga.html create mode 100644 front/src/components/app-orga2.html create mode 100644 front/src/components/app-view.html create mode 100644 front/src/reactivity-shell.html create mode 100644 front/src/shell/navbar/idea.png create mode 100644 front/src/shell/navbar/navbar.html create mode 100644 front/src/shell/navbar/navbar.js create mode 100644 front/src/shell/pages/404/polymerosaurus.png create mode 100644 front/src/shell/pages/404/view-404.html create mode 100644 front/src/shell/pages/organisation/organisation.html create mode 100644 front/src/shell/pages/organisation/organisation.js create mode 100644 front/src/shell/pages/organisations/organisations.html create mode 100644 front/src/shell/pages/organisations/organisations.js create mode 100644 front/src/shell/pages/settings/settings.html create mode 100644 front/src/shell/pages/settings/settings.js create mode 100644 front/src/shell/pages/views-wrapper.html create mode 100644 front/src/utils/eventSource.util.js diff --git a/front/.eslintrc b/front/.eslintrc new file mode 100644 index 0000000..1cc8ff1 --- /dev/null +++ b/front/.eslintrc @@ -0,0 +1,27 @@ +{ + "parser": "babel-eslint", + "ecmaFeatures": { "modules": true }, + "env": { + "browser": true, + "node": true, + "es6": true + }, + "extends": "airbnb", // supposedly good, ajusting some values below. + "rules": { + "arrow-body-style": ["error", "as-needed"], + "jsx-closing-bracket-location": 0, // doesn't always improve readability + "react/jsx-space-before-closing" : 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], + "comma-dangle": 0, // too much + "no-console": 0, + "spaced-comment": 0, // sucks for code comments + "max-len": ["error", 160], // I don't code on my smartphone, need more than the default 80 + "no-param-reassign": ["error", { "props": false }] + }, + "settings": { + "import/resolve": { + "extensions": [ ".js", ".jsx" ] + }, + "import/parser": "babel-eslint" + } +} \ No newline at end of file diff --git a/front/.gitignore b/front/.gitignore new file mode 100644 index 0000000..e6dd7e0 --- /dev/null +++ b/front/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +/bower_components/ +/.idea/ +/build/ \ No newline at end of file diff --git a/front/README.md b/front/README.md new file mode 100644 index 0000000..a873cb6 --- /dev/null +++ b/front/README.md @@ -0,0 +1,19 @@ +## Reactivity web-front + +## Dev +### Install Dependencies + + +#### /!\ some dep need ssh key on github (windows shell can't open passphrase prompt !) /!\ +```sh +npm install +``` + +### Launch server with panel +```sh +npm start +``` + +### This apps need a server to init SSE connection an provide data. + +plz check the express-sse-with-electron-panel project in the same repo. \ No newline at end of file diff --git a/front/bower.json b/front/bower.json new file mode 100644 index 0000000..b61ae35 --- /dev/null +++ b/front/bower.json @@ -0,0 +1,22 @@ +{ + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "author": "Nathan DAMIE", + "dependencies": { + "polymer": "Polymer/polymer#2.0-preview", + "paper-card": "PolymerElements/paper-card#2.0-preview", + "iron-icons": "PolymerElements/iron-icons#2.0-preview", + "iron-icon": "PolymerElements/iron-icon#2.0-preview", + "iron-flex-layout": "2.0-preview", + "iron-collapse": "2.0-preview", + "paper-button": "2.0-preview", + "paper-checkbox": "2.0-preview", + "paper-icon-button": "2.0-preview", + "app-router": "blasten/app-router#master" + }, + "resolutions": { + "polymer": "2.0-preview", + "webcomponentsjs": "v1" + } +} diff --git a/front/gulpfile.js b/front/gulpfile.js new file mode 100644 index 0000000..2218eab --- /dev/null +++ b/front/gulpfile.js @@ -0,0 +1,28 @@ +const gulp = require('gulp'); +const browserSync = require('browser-sync').create(); +const proxyMiddleware = require('http-proxy-middleware'); +const historyApiFallback = require('connect-history-api-fallback'); + +const options = { + target: 'http://localhost:8080', + changeOrigin: true, + pathRewrite: { + '^/api': '/' + } +}; + +// Watch scss AND html files, doing different things with each. +gulp.task('default', () => { + // Serve files from the root of this project + browserSync.init({ + server: { + baseDir: "./", + index: "index.html", + middleware: [ + historyApiFallback(), + proxyMiddleware('/api', options) + ] + } + }); +}); + diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..4c628fa --- /dev/null +++ b/front/index.html @@ -0,0 +1,44 @@ + + + + + + + Reactivity + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/front/manifest.json b/front/manifest.json new file mode 100644 index 0000000..40c8fba --- /dev/null +++ b/front/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Reactivity", + "short_name": "Reactivity", + "display": "standalone", + "theme_color": "#3f51b5", + "background_color": "#3f51b5", + "icons": [ + + ] +} diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..d40c3da --- /dev/null +++ b/front/package.json @@ -0,0 +1,22 @@ +{ + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "main": "main.js", + "scripts": { + "postinstall": "bower install", + "start": "gulp", + "lint": "./node_modules/.bin/eslint ." + }, + "author": "Nathan DAMIE", + "devDependencies": { + "bower": "^1.8.0", + "browser-sync": "^2.18.5", + "connect-history-api-fallback": "^1.3.0", + "gulp": "github:gulpjs/gulp#4.0", + "http-proxy-middleware": "^0.17.3" + }, + "dependencies": { + "lodash": "^4.17.4" + } +} diff --git a/front/src/components/app-card.html b/front/src/components/app-card.html new file mode 100644 index 0000000..4c944d8 --- /dev/null +++ b/front/src/components/app-card.html @@ -0,0 +1,1440 @@ + + + + + + + diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html new file mode 100644 index 0000000..a784c30 --- /dev/null +++ b/front/src/components/app-orga.html @@ -0,0 +1,226 @@ + + + + + + + diff --git a/front/src/components/app-orga2.html b/front/src/components/app-orga2.html new file mode 100644 index 0000000..1d402a5 --- /dev/null +++ b/front/src/components/app-orga2.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + diff --git a/front/src/components/app-view.html b/front/src/components/app-view.html new file mode 100644 index 0000000..3e10e5c --- /dev/null +++ b/front/src/components/app-view.html @@ -0,0 +1,24 @@ + + + + + + + diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html new file mode 100644 index 0000000..d2ac8df --- /dev/null +++ b/front/src/reactivity-shell.html @@ -0,0 +1,61 @@ + + + + + + + + + + diff --git a/front/src/shell/navbar/idea.png b/front/src/shell/navbar/idea.png new file mode 100644 index 0000000000000000000000000000000000000000..e65b6587696a80aa728b2cb0a7b37cbb46ea2c50 GIT binary patch literal 112692 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%0A+70|6Jra*EdGmbSDu= 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�-|)VK`U%Fow=|W)Ua3@WH4Pw=&a} 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 + + + + diff --git a/front/src/shell/navbar/navbar.js b/front/src/shell/navbar/navbar.js new file mode 100644 index 0000000..7ca67b6 --- /dev/null +++ b/front/src/shell/navbar/navbar.js @@ -0,0 +1,13 @@ +class ShellNavbar extends Polymer.Element { + static get is() { + return 's-navbar'; + } + + handleClick (e) { + e.preventDefault(); + document.dispatchEvent(new CustomEvent('nav',{detail: {path:e.currentTarget.pathname}})); + } +} + +// Register custom element definition using standard platform API +customElements.define(ShellNavbar.is, ShellNavbar); \ No newline at end of file diff --git a/front/src/shell/pages/404/polymerosaurus.png b/front/src/shell/pages/404/polymerosaurus.png new file mode 100644 index 0000000000000000000000000000000000000000..28201c9ba3869a13d49a07cffd8831d9b089a8f0 GIT binary patch literal 8469 zcmYM4cTiK`^Y=k8hym6Qj4;T@8kczRPQ;2D78OkvaoIbu8N<`s}IA>ZyzLW?wS z$rXdA1eWNSO{n$61fjZ(AIik%2Jy7dWM%0CFu##7w|95>@+Rr#$G$6)-u`LV2Eow-~`G#0L>H=g5y&gY^Bgw{LZWFzGyL_t?<(LJ4km6e8YaUYjs%rY6zkBTp$? z9v#lccmfHbt2Tq5a%00LM|WE}J8v(*DbtsL>|!0CCC`eHrNh31F@}6`+GzcUiJ~1E zSR2ITTUaPm^A=7~7Cq|O)!0RGL*Zdcann~3<>)WaAI~OhX%6y4A~;RtiV5kgM?$V3 z-_ko6dGHR^0q@h~glb*nOXCF-VdMef*Ncw_%f#Wb65977@Vqz%qwb|#B-7G6%0;1H zFjBhY^KD0IybdO%b&MrO{N8g?I^36&^A0A!MOh}jfYYY=vF@0=;U(NR;V}E5{;c>Z z0rEv&Z>n$ZU6=pV91*A#T;|5zjJm4l=9Eku;5C+o9P9$6@&QhYi3Ln%;eTc)s|+(; zNT7vVWN^Fp@xJ=2+(lFwPzY`4%>?nMF7*m>AK0y4h{j>|iRIs#CM9)q{x&+t-aHB1++#|!1V<_*dV!g9OTWNOWB)Nk;N(0r>yS0+W+&tqoXrcNXYI} z@Reg#)&)6ACa^aF1?o2=sD}9F-OE;0!F~)n*!{EIoAIadsv;&v2@JAO>ViRQNG)hJ z%e`9~Eg>hz-6g^ETPywFtM%Ml1-#+8G5Pd~i2LNZAKyELLIP`wZFKZI0*I9`yygf* zvUiu{f{WvZ&j0$_@O@KnmeqsS9ifo$TWJ6}IJ|NJ@i_wRCsz!+_Nt#fg(;}tvi`@Y z`o1NXovi#aN=rbGj=~L7PI4ytasrR`5z4!IbV-jG*zKI#Q>UI%g|hpVb}E5;f0s_5 zssH`6jdlzh7-Os(6_amJqyTy4T#eH8vqoEWfxOLpww48r){%s|I}~DKuFd7$30;>C zIE99Hs&ikD$+zL>4N}6;x49+vlZ*R}4xo@?}E z1dQBFA3r}H{4B71cVgQmdh#4lYM(zgmrY~6RZI|I5BETi!|pvl?ecNlvQ#AV7Gl9mb!$bCN@ zY2%H4Jm*^C9E)yJ_t>Qf^}s^y?f8X}*V%eis?cesxtDZOUoP9N=El>x(j8YXB(=Sm zmV?qip9YGQiKe`G(N#a)0HHL}Cz?vKFGP`+@+f}oeWm(4B5t}aL!wA@uW`jNIxzVB~11XesdW@|%>VqK)B@llK* z+)SD+V^DL&@mJvTdfTgVAP8UL=77(M6^JX=k5TQ$8+FIS+}5e}^e=Vuz7pYbm9zNJ z`Ia&4{<$s1Wx$!p$zZ3Ho_RX6jrGvgEx*)PB4}GQe%Hs)+1kPbjtPuGhSap- zWSSF)skvVW3z+SNWYN}Ih?jCxmvRCl`FRM* z-AdNpU27?p0maLHov}>yI>Qwa)p{M zNvC4`j3{m#j7Wgsd3H%KkC4+FU{4#roE0x#p2r*wG`D|)X{nz6W0kCvpEzc%b8brG zn^N@T<+tUmhi$4_@M|*{UuUAf7$%3M7ifuoeLFS(d`~M@7V$K;aZdT3-=RfXWob`5 zSYSBGsWlql^1t}$j@MurwmoyBmm#jp2_dQ$#u~qh^?4ss`;2i!ycTp@qcbK&+4Yan|hxPSiQt_y6KNx1Fy#upG5Bvi8l#U zsbIl0UV7|7f!a7kw!_cI3#W@X(v8VZ=N=+zYrXjW{AUa)mN78RQ6&;Yx5tz*GP@2B~5a;q}z$eXLLlD#SH5vMFHaw zOteF09`$lqwCU`7-X+l2iv)>l4q+*6CQt3Y(&2an^X)e4#3!ckT9Dg)l4Vm&lcyS? z9#)2?=2+CRu~6jC%9XI1DOw*e47-@t^dlrKWq1_uTp(__XSLhEwofMPJ(EJF=L!JY zwk8QfJ&#LUeVE#e#w>p~t$8UtNxKCkwC|vE2V2bRYShy@)N|YiZy?zK)Et*lnkw87 ziu2>cbs=ih!DOy<1atya<+wpkUVhay0!asU18_8sI8$vVXq0*vOqPF`&b(EyG18vN z8C$5eIOWUuxW9{x1+_fNP1nZRK>h<(iSG5JbH~EaJDUw z@Uj2$0@Z=jo89*$K=$e){Cb9&U9rs+FgA&NjpkQL$I(d8R&UJ|wf>=oZ$W5g0|SYQ zvw#FS?!IL0rwP3}$vt&uN$z{Ld#DE9jl`Ajd%jotvS?C+Zm(L^Y^vm*`(+#SmMWqH zam!e5cL-K((uB9BfM$&TQYro| zWjY6ke8j53dUGT*+lI0zacCtIb{7tb*sZSXyA`8!^%ij@T?cF1g6NCITXW*7-CHwy z@`8#B4AzJY1qQKctz7I;L+=B7yO1eO%IQ~NCMg=uZ8AN<7K|D#Pd4hAzVx=m@xtQ9$CH`T{L@5l?_fAK zV4;g}w%Nh@y;N!foH{`Gd6)X$_=YAU2#(T2Ol$}!n52d!p z4J>qO4P{p*>XZFP7MKn+d!zwd@%9NO3lWA5V?{)3f}wt-YW^DvaM(6e(%vR&OD&PM z@?eNfQ{O-H!EiR#X8*mNTv?s~uf54qfaJ#g7*j80x_h@mSXb@8XO*%H!;Gl0+OaKR zV~Qqg5k6_>rd*+#T2Fs=$he5N+Kovu+pm9BgdH5HMXS$D%_*?y}8YS(C z4{TO97v#HrB$-AdYPuQ)nbeNPoj2FlguWwW;?YH2jrIN>(Q=616(V%UlZ#dNx?u4Z zk{vR&H|@r2MBz#HL-tROst*noKiPd}&yRik`D(wB!iReKC~7-oKI8(=*w|;Tp7r;+ zN0DWRPsHzL>U9qEr=BLvS+od=0IUTvwltJQhh>WQh!3c@5&N3)*gE(I19^Dsk?DR6 z7Y^C7d;NX>)9!5K8%!#|;D&wc{;XBTV|ae0gGqt^gQJX4{IznbGXRP&3iZ`DFw#+s zpUt~EJT%fv2d{o9r5{wEmhTDSf()aE8;As!{3eP2h~Chi+E>0sRpm^-AtjERS$`ZN%w~vY7)8p zi;iq^ot02YIB|?w$G%AMI8C4BDDzlPe!>-g8K}Nccu|6~Uet6v7__kI{rhb?3IAHT z(|RjJ4_~~hWQ6faNs}wxc@6lTyX*)r{7`s44~6x={n9UK!_U3GRm)N}m|6 zcB^SwFl{Vz)b1DhZx%J7^6)`EZ*}F0JiExB?KAXrb(%Vv{pAjD!!^2&aOuk#{JZfO z*V_vJVBv`6ie7K6J*&`JdJ}4^S)`dj61kPvo7U@3P$z|!XgANvxw5l~l7*R8rm;fl zF{$%=s7En_aPrgkLAz;UF1?2L$~}YMFTm=<``QKOg{$Ah?(k{q5kHrR1j&xLc-H5J zq1&wQNpp|aJrNK=Ju2<4+2${Ts>1KQ>0sEQyDDw@{y5XC(HQS2qTip!=$J^0^l%>N z=iXHasE0!Uk7Jy_wv)>6#vkyX^Dx@$Uv3C;z zp9;RO#o0M2F@Ps9Mmu#uCH$gHMcpntFiq0nt&XAfI_W&c8-xCz8lvBwjhE_A-${V6 z2gw&UtdHKpF!Xi%{Z;Dz@Nd$th;;<(v$W~-GkCok2T%xidb0IXWq3x4iEFgt^JP{* zYD&u}lTzBy#9eQHwk}zqjAlK#&=i+qSi(P!p+`cBl%MML2*YLi-j5v882sbYRGKCu zlClNfelZfUC)ZzR8Eao3wTDfHA%!k2I2 z0z-_`^N6GsO>-Ko%;_>Px#h#%c|!81YVAmIWO|zLl!x5?77*`?&xXYX+2_SFE*W+c zXL)tPy2||JWhTB>HFA7zo%r^MOU%DdY8k}4x>?}sf!Vl+BTq$CXIPX52=FplnD6U; z7)QqJas#C!4Q0}w1UOvipn@qDycu=>n)z0h3I~Y7>jI&)K$n^f?(sbOznhn6DYkzg zLjgdksEi7A{~H|+yrnSPGY#WzPHZDpjSpR=4onRIJ5zSoV#mFW)ooKc$3cqya@P{6 z^RT{>TiQ(MdmU6JM;E-KNjziO70J8Wm7Uy;PfgWa^ar0mWCNr8KJsrN9jw6)Za;&z z1YsU;-n&F!Aq0SU)!Vj4whKYnIvHh0?Mm}UdRD`+uU~;ZXPWM1<{e(C#L1#&M*~N2 zG3aDYgQj^X%c3CRi=mZjAysNm_Eu|?YJ4OH>V05(jz^T~d-}Rx-PH#!7r}iGvo4H+ zp#3(gB+4RvW-#7{${Zeld%178voaO&4Gvd|}+V zYr6AuUGGs*fs@1sY;sEHs^7nclpM{8Dw>!9FzVZl`ly2Wu@Rn=0`vt(1UHz3&5g+0nA>jh`;UvE4WMo8ZT4v3TY)4uzA z-m4kInwV?R!&vdFSz9PyU}w=_QfzjpY8Kt?dRH8pE81NBzKc5p2UjvjME0gXEeJ&Y z(Y31oh)d>kZdM23iRnnyxn}Ve?h$D=2ZEqKyCyhilK1x`AGPIkayDjEw6T8$FStcX z*u}c8-NZ-X%TJX09gbhdc*4`x-~9ttNm84j-;+Z%2x+}u?KF(FpEsK0gJc}4@P}sQ zg^uYO4~zp*ausI`=A&OzJQ?SV>qQr^Md%KDdZTOHf$YdoNe!nv-}5=6e17B%2&>$n{@rYe3!S zqnS}FSH%|o@rN#ED&>_TEWF)_Nz@4q8?4u|hL$=)feoK09h;p9Q?jpQsy3sr{(TvR zWN*374ngE}73s&TD1nSvCqGdlV5MzNwc~La^X591R1!{K-kk`Fan`7vzlpPOdEC`A z23R$BajTt>K-1*v(k_y;Zj`v?r~VzPL)Re}+9w;F&&%Ziki~8m;>>8Qk3zX?9#0y5 zCe3Kz5r-tagnPy@d8v}CNn(&H*J6T64}~oWXTVDmM1~*d*u^@HPA=t40^xb6JZY)k zilmJ4aR;X_lf>Txy)UuOSq~~hXG0cQdwh&IP>Qpa;n!RLcwm|f!;#4}j$t1{!v#}= zR-vZ~q%%M{y4kemBuj5a<8c-wI;}M*=_sVfzo?8iQs0jAwZZ!>fs6 z_<1gHHOb3UerjU%F^`>{qMO*_MMfjSU)%EYW9r3E4{Osi-ar#Wd#a^lA(Kj=5N9F` zohoj|bQQdVkyPRtWw{Eu{{z&D!Lthf1574$`Hn@H{|_+5nbwE$Lj4bLOO)&^@*iMX z3qP!%X}35u03v60IiX$`a{dRPU4s+H{sTB`V4*e<|Bp%&gQu2%9*#V0S)veciJDKE zsXPBuI_-LpA`Nk_o3)~jND9$eRC7{O`H9GXYcEl;Ndx7Uh?D1SAdJI*hWd@3le%OW zp4g?kd-29Q&V!n0n8acbRy5$pXzBd|w20zd3{1prDp3ABB0(kjVMn6sUnY!U_MgFv z`tRtmCsV1Fdj0s#(~A0{P<63e@n6JMzq$e98}#$#wq{4ASEeaCo05n`m8+F)1e{^v zVVqB8+iP@?ZgY1{96xm|VWj~L9+vufJNGh#<>#!uts<#_P*X=;F|Ik)VK}{r-)%;u z!v8NxZI@Bon(3}^$&rN1_}~0aozbZ7 ze7%JZB-%3*{vCo4sbkXF>v|t_=oQgx&jsXsxsiPqVBM!X4t)EsYLqrJu|{>`V=?L_ zZM7DA%3*})5fE7{$?n!oSg8`z#VxQ5Q+Fn`0WqEV(1?^wU6GW>O;df-<1`!k7Ts!5e>>8COO^_$U1GRrI7z3ID@811<2%RoXvKN%eN;p z%2q~>#oHh5p&W`R#F&BJ4B&vQqUIZ%&4(1Jybn?jY}`Osx=Wz$9zX<%2NZovmrj+C z)I^Xq&5e#T!Wa?RP1611?G_DP@E90dle675G$7z5S|{7|vP_4&p)q@r)sm7{=BH^> zkLWe#8Dam*7yOS3?@XR#}o;070_I0%zIlETJdW zO&4)R0_zkXj^+GNdR7eHQ0#{*ZaDn`hcU=f$}ozgM$%pGcHQheWdmi|xG1gt8}X0O z-0{vc*!zejd%r_{^t^INRP04C#_BN!5C-K4Zg@t5VNv~N-ezl?0_S*o=6aSsrowK^ z)hbFyB}iQuPyr!i5k&>*PVN0%03E9z@FC*coaw=se9ifr07*c-W&b+f66)Ih;~RH5 zRE`$)ckr~#fBU%?d(d!uTo(ZftErk^cZS(N#0I-S0$g->guILzc~9T1u(v~NJWPwJ z#!6W=1B?W>57ZFg^1#@YPfFS8T`HE&_8&qVZ74s_k=l39r2o+rvY)k;RlVJqtn)Di zfS#ZbZ^yGomqoVSY%Uqscj0c)e@IW9j#DIwL1FsJ`XAk}u07oX#jtHi&@Vk(0SryO zOGO4wp6=0*{|8eXHCmhWRb6j$-FN9vnXtx=?aK z&tqKj0TX6d{Oqg%`)7@f#9aISeUUZqu#XZdIcop%vgd&QhzLOovi`rQ@sq

                +}=) zzpOGB?do^?Nf*DM#)<|>0T_SbndmO-T!DRz*~N3F48KYq9vz%~0R3~}rJMb-D^uHB zno~Rc<#BvFFvM!yVJMn%HnHN(yJvce7}=SFbrHT7X}KSC{YCPkf}M^=KOJ`Ve!D+B zVN>^hw)_0a_V~&EpJ$(r=KcgBQn6@zRw04=5D>nr3D zlX{cAK*aun+Q}$JV%3e>Os*;ZuC$#H-U~V64xPUv$oZCr$|+|rt-nz4SORxN7wdG@ z=+g-yd7E#jcJ+>H!SPR_;H`GZh>uI%vJCe44`0aMKh*K^+_6mH+mt7qL0_u6%5oo~ zWVF*RfxE_XowLZ2ss3Y;pLKqALe?$0Puov?Qr13Ib$5t53JK*}fG;J2{a>tJuRgIJ zW7M9@Jgbtvy^xJIVj1|-FCeugog=Wp_E}XT{p(sy1@DSTzSOek_+EkUn+i2c)lN-?SNP5^2;-Q)sxF6?Na_ z;(_G;&AzSWUX9}l@+_-6s#pXA(S~ZNb9YnxjJ4^v%#i%-YhJ^P5r>FTlE=%ONALLM oep{M89neq+t=OCFKNh+pc{QJ5jJNRguLTA@ZDXwp4f~h>2eZ8|ZvX%Q literal 0 HcmV?d00001 diff --git a/front/src/shell/pages/404/view-404.html b/front/src/shell/pages/404/view-404.html new file mode 100644 index 0000000..8cab5bc --- /dev/null +++ b/front/src/shell/pages/404/view-404.html @@ -0,0 +1,77 @@ + + + + + + diff --git a/front/src/shell/pages/organisation/organisation.html b/front/src/shell/pages/organisation/organisation.html new file mode 100644 index 0000000..aca70c6 --- /dev/null +++ b/front/src/shell/pages/organisation/organisation.html @@ -0,0 +1,10 @@ + + + + + diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js new file mode 100644 index 0000000..e0b97fd --- /dev/null +++ b/front/src/shell/pages/organisation/organisation.js @@ -0,0 +1,33 @@ +class ShellOrganisation extends Polymer.Element { + static get is() { + return 's-organisation'; + } + + connectedCallback() { + super.connectedCallback(); + + if(this.id) { + var req = new XMLHttpRequest(); + req.open('GET', `http://localhost:3000/api/subscribe/${this.id}`, true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if (req.status == 200) { + this.data = JSON.parse(req.responseText); + var groups = _.groupBy(this.data, "event"); + + + //TODO:DO something with that !!! {{info}} + + + + } else { + alert("Erreur pendant le chargement de la page.\n"); + } + } + }; + req.send(null); + } + } +} +// Register custom element definition using standard platform API +customElements.define(ShellOrganisation.is, ShellOrganisation); \ No newline at end of file diff --git a/front/src/shell/pages/organisations/organisations.html b/front/src/shell/pages/organisations/organisations.html new file mode 100644 index 0000000..12acf9d --- /dev/null +++ b/front/src/shell/pages/organisations/organisations.html @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js new file mode 100644 index 0000000..4cd8b81 --- /dev/null +++ b/front/src/shell/pages/organisations/organisations.js @@ -0,0 +1,28 @@ +class ShellOrganisations extends Polymer.Element { + static get is() { + return 's-organisations'; + } + + constructor() { + super(); + this.organisations = []; + } + + connectedCallback() { + super.connectedCallback(); + var req = new XMLHttpRequest(); + req.open('GET', 'http://localhost:3000/api/load/organizations', true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if(req.status == 200) { + this.organisations = JSON.parse(req.responseText); + } else { + alert("Don't be silly launch the server !\n"); + } + } + }; + req.send(null); + } +} +// Register custom element definition using standard platform API +customElements.define(ShellOrganisations.is, ShellOrganisations); \ No newline at end of file diff --git a/front/src/shell/pages/settings/settings.html b/front/src/shell/pages/settings/settings.html new file mode 100644 index 0000000..797cde3 --- /dev/null +++ b/front/src/shell/pages/settings/settings.html @@ -0,0 +1,15 @@ + + + + diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js new file mode 100644 index 0000000..b60a0aa --- /dev/null +++ b/front/src/shell/pages/settings/settings.js @@ -0,0 +1,6 @@ +class ShellSettings extends Polymer.Element { + static get is() { + return 's-settings'; + } +} +customElements.define(ShellSettings.is, ShellSettings); \ No newline at end of file diff --git a/front/src/shell/pages/views-wrapper.html b/front/src/shell/pages/views-wrapper.html new file mode 100644 index 0000000..77786e8 --- /dev/null +++ b/front/src/shell/pages/views-wrapper.html @@ -0,0 +1,96 @@ + + + + + + + + + diff --git a/front/src/utils/eventSource.util.js b/front/src/utils/eventSource.util.js new file mode 100644 index 0000000..c7d82e0 --- /dev/null +++ b/front/src/utils/eventSource.util.js @@ -0,0 +1,77 @@ +window.EventSourceManager = class EventSourceManager { + constructor(url, manager) { + //Create an Observable for the EventSource Connection + this.sourceObservable = Rx.Observable.create((observer) => { + this.source = new EventSource(url); + + const onOpen = (e) => { + observer.onNext(e); + console.log("%c SSE reconnected", "font-size:22px;text-shadow: 0 0 3px #FF0000, 0 0 5px blue;"); + + // this.source.removeEventListener('open', onOpen, false); + }; + + //TODO : On error subscribe to a new stream (wso ) + const onError = (e) => { + if (e.eventPhase === EventSource.CLOSED) { + observer.onCompleted(); + } else { + observer.onError(e); + } + }; + + const onMessage = (e) => { + observer.onNext(e); + }; + + this.source.addEventListener('open', onOpen, false); + this.source.addEventListener('error', onError, false); + this.source.addEventListener('message', onMessage, false); + + //Add listener on specific eventType + this.addEventsObservables(manager); + + return () => { + this.source.removeEventListener('error', onError, false); + this.source.removeEventListener('message', onMessage, false); + this.source.close(); + }; + }); + } + + static get source() { + return this.source; + } + + static get sourceObservable() { + return this.sourceObservable; + } + + /** + * Create an observable for each property of the given manager + * @param {Object} manager + * @returns {void} + */ + addEventsObservables(manager) { + //for each property + for (let eventType in manager) { + if (manager.hasOwnProperty(eventType)) { + + //Create a new observable + const observable = Rx.Observable.create((observer) => { + const onMessage = (e) => { + observer.onNext(e); + }; + + //listen the eventType on the EventSource + this.source.addEventListener(eventType, onMessage, false); + }); + + //Subscribe the callback. + observable.subscribe((e) => { + manager[eventType](e); + }); + } + } + } +}; \ No newline at end of file From dd16067a4ac751a6c92a88055bda5999784f5bf4 Mon Sep 17 00:00:00 2001 From: nathandm Date: Mon, 9 Jan 2017 16:18:44 +0100 Subject: [PATCH 039/132] front: infinit scroll for "LIST" view type. - impl a lazyloading scroll list - mixin for global var --- core/broadcaster/pom.xml | 52 +--- .../broadcaster/config/CouchbaseConfig.java | 49 +--- .../core/broadcaster/web/EventController.java | 7 +- .../src/main/resources/mocks/index.html | 24 +- front/bower.json | 3 +- front/index.html | 4 +- front/package.json | 3 - front/src/components/app-artifact-list.html | 49 ++++ front/src/components/app-orga.html | 267 ++++++------------ front/src/components/app-orga2.html | 143 ---------- front/src/components/app-view.html | 51 +++- front/src/constants/const.mixin.js | 5 + front/src/reactivity-shell.html | 116 ++++---- front/src/shell/navbar/navbar.html | 2 +- .../pages/organisation/organisation.html | 22 +- .../shell/pages/organisation/organisation.js | 16 +- .../pages/organisations/organisations.html | 22 +- .../pages/organisations/organisations.js | 4 +- front/src/shell/pages/view/view.html | 26 ++ front/src/shell/pages/view/view.js | 59 ++++ front/src/utils/eventSource.util.js | 77 ----- 21 files changed, 421 insertions(+), 580 deletions(-) create mode 100644 front/src/components/app-artifact-list.html delete mode 100644 front/src/components/app-orga2.html create mode 100644 front/src/constants/const.mixin.js create mode 100644 front/src/shell/pages/view/view.html create mode 100644 front/src/shell/pages/view/view.js delete mode 100644 front/src/utils/eventSource.util.js diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 261ee2c..1b187f5 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -82,12 +82,26 @@ ${project.version} - + io.projectreactor reactor-core ${reactor-core.version} + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + + org.springframework.boot @@ -129,7 +143,6 @@ org.springframework.boot spring-boot-maven-plugin - ${spring-boot-dependencies.version} @@ -141,41 +154,6 @@ - - - - netty - - true - - - - io.projectreactor.ipc - reactor-netty - - - org.springframework.boot.experimental - spring-boot-starter-web-reactive - - - org.springframework.boot - spring-boot-starter-tomcat - - - - - - - tomcat - - - org.springframework.boot.experimental - spring-boot-starter-web-reactive - - - - - diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 1490e09..6465318 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -22,13 +22,6 @@ * This configuration initializes couchbase connection. *

                * - *

                - * See the class constants for properties that can be configured and their default values. Node that properties can be - * configured in several ways thanks to Spring Boot property resolution. For instance, you can run the application with - * program argument {@code --reactivity.couchbase.nodes=xxx} to define the {@code reactivity.couchbase.nodes} property. - * More details here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html - *

                - * * @author Guillaume DROUET * @since 0.1.0 */ @@ -36,25 +29,8 @@ @ConfigurationProperties(prefix = "reactivity.couchbase") public class CouchbaseConfig { - /** - * Default value for property {@code reactivity.couchbase.nodes}. - */ - public static final String DEFAULT_COUCHBASE_NODES = "127.0.0.1"; - - /** - * Default value for property {@code reactivity.couchbase.bucket}. - */ - public static final String DEFAULT_COUCHBASE_BUCKET = "artifact"; - - /** - * Nodes to use. See {@link #DEFAULT_COUCHBASE_NODES default} value. - */ - private String[] nodes = new String[] { DEFAULT_COUCHBASE_NODES } ; - - /** - * Bucket name. See {@link #DEFAULT_COUCHBASE_BUCKET default} value. - */ - private String bucket = DEFAULT_COUCHBASE_BUCKET; +// private String[] nodes = new String[] { "127.0.0.1" } ; + private String[] nodes = new String[] { "ec2-35-160-191-149.us-west-2.compute.amazonaws.com" } ; /** *

                @@ -63,10 +39,18 @@ public class CouchbaseConfig { * * @return the {@code Bucket} */ +// @Bean +// Bucket sync() { +// final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); +// final Bucket bucket = cluster.openBucket("artifact"); +// bucket.bucketManager().createN1qlPrimaryIndex(true, false); +// +// return bucket; +// } @Bean Bucket sync() { final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); - final Bucket bucket = cluster.openBucket(this.bucket); + final Bucket bucket = cluster.openBucket("default"); bucket.bucketManager().createN1qlPrimaryIndex(true, false); return bucket; @@ -82,15 +66,4 @@ Bucket sync() { public void setNodes(final String[] nodes) { this.nodes = nodes; } - - /** - *

                - * Sets the bucket where data are managed. - *

                - * - * @param bucket the bucket name - */ - public void setBucket(final String bucket) { - this.bucket = bucket; - } } diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java index 01f33e0..db51500 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java @@ -23,10 +23,7 @@ import io.reactivity.core.lib.event.Organization; import io.reactivity.core.lib.ReactivityEntity; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; /** @@ -52,7 +49,7 @@ public class EventController { *

                * * @param viewId the view ID - * @param limit maximum number of returned artifacts + * @param limit maximum number of returned artifacts * @param maxAge highest possible age for an artifact * @return the event flux */ diff --git a/core/broadcaster/src/main/resources/mocks/index.html b/core/broadcaster/src/main/resources/mocks/index.html index f2529b8..8933875 100644 --- a/core/broadcaster/src/main/resources/mocks/index.html +++ b/core/broadcaster/src/main/resources/mocks/index.html @@ -7,9 +7,9 @@

                Events: -

                  - - +
                    + +

                    + diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html index a784c30..1d402a5 100644 --- a/front/src/components/app-orga.html +++ b/front/src/components/app-orga.html @@ -1,191 +1,117 @@ + + + + + + + + + + - - diff --git a/front/src/components/app-view.html b/front/src/components/app-view.html index 3e10e5c..5a6c0c1 100644 --- a/front/src/components/app-view.html +++ b/front/src/components/app-view.html @@ -1,10 +1,47 @@ - - diff --git a/front/src/constants/const.mixin.js b/front/src/constants/const.mixin.js new file mode 100644 index 0000000..eaba4dd --- /dev/null +++ b/front/src/constants/const.mixin.js @@ -0,0 +1,5 @@ +let GlobalConst = (superclass) => class extends superclass { + get wsURL(){ + return "http://localhost:3000/api"; + }; +}; \ No newline at end of file diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index d2ac8df..730e21e 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -3,59 +3,65 @@ - - - - + + + + + + diff --git a/front/src/shell/navbar/navbar.html b/front/src/shell/navbar/navbar.html index 464dbe3..5684509 100644 --- a/front/src/shell/navbar/navbar.html +++ b/front/src/shell/navbar/navbar.html @@ -18,7 +18,7 @@ padding: 0 64px; overflow: hidden; justify-content: space-between; - box-shadow: 0 1px 7px 0 black; + box-shadow: 0 5px 10px rgba(0,0,0,.15); } .horizontal-wrapper { diff --git a/front/src/shell/pages/organisation/organisation.html b/front/src/shell/pages/organisation/organisation.html index aca70c6..4e639c6 100644 --- a/front/src/shell/pages/organisation/organisation.html +++ b/front/src/shell/pages/organisation/organisation.html @@ -1,10 +1,28 @@ + - diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js index e0b97fd..57b5857 100644 --- a/front/src/shell/pages/organisation/organisation.js +++ b/front/src/shell/pages/organisation/organisation.js @@ -1,24 +1,24 @@ -class ShellOrganisation extends Polymer.Element { +class ShellOrganisation extends GlobalConst(Polymer.Element) { static get is() { return 's-organisation'; } - + constructor (props) { + super(props); + this.views = []; + this.artifacts = []; + } connectedCallback() { super.connectedCallback(); if(this.id) { var req = new XMLHttpRequest(); - req.open('GET', `http://localhost:3000/api/subscribe/${this.id}`, true); + req.open('GET', `${this.wsURL}/subscribe/${this.id}`, true); req.onreadystatechange = () => { if (req.readyState == 4) { if (req.status == 200) { this.data = JSON.parse(req.responseText); var groups = _.groupBy(this.data, "event"); - - - //TODO:DO something with that !!! {{info}} - - + this.setProperties({views : groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); } else { alert("Erreur pendant le chargement de la page.\n"); diff --git a/front/src/shell/pages/organisations/organisations.html b/front/src/shell/pages/organisations/organisations.html index 12acf9d..b0226c5 100644 --- a/front/src/shell/pages/organisations/organisations.html +++ b/front/src/shell/pages/organisations/organisations.html @@ -1,4 +1,4 @@ - + - - diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js index 4cd8b81..38e51db 100644 --- a/front/src/shell/pages/organisations/organisations.js +++ b/front/src/shell/pages/organisations/organisations.js @@ -1,4 +1,4 @@ -class ShellOrganisations extends Polymer.Element { +class ShellOrganisations extends GlobalConst(Polymer.Element) { static get is() { return 's-organisations'; } @@ -11,7 +11,7 @@ class ShellOrganisations extends Polymer.Element { connectedCallback() { super.connectedCallback(); var req = new XMLHttpRequest(); - req.open('GET', 'http://localhost:3000/api/load/organizations', true); + req.open('GET', `${this.wsURL}/load/organizations`, true); req.onreadystatechange = () => { if (req.readyState == 4) { if(req.status == 200) { diff --git a/front/src/shell/pages/view/view.html b/front/src/shell/pages/view/view.html new file mode 100644 index 0000000..78df1e3 --- /dev/null +++ b/front/src/shell/pages/view/view.html @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js new file mode 100644 index 0000000..4d87943 --- /dev/null +++ b/front/src/shell/pages/view/view.js @@ -0,0 +1,59 @@ +class ShellView extends GlobalConst(Polymer.Element) { + static get is() { + return 's-view'; + } + constructor (props) { + super(props); + this.limit = 50; + this.artifacts = []; + //load on init + this.isLoading = true; + this.maxage = -1; + } + connectedCallback() { + super.connectedCallback(); + + //throttle : function that only invokes func at most once per every wait milliseconds (350) + this._scroll= _.throttle(this.scroll.bind(this), 350); + this.$.content.addEventListener('scroll', this._scroll); + + if(this.id) { + this.fetch(this.limit, -1, (data) => { + this.setProperties({artifacts: data}); + }) + } + } + scroll(e) { + if(this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { + if (!this.isLoading && this.maxage ) { + this.fetch(100, this.maxage, (data) => { + this.setProperties({artifacts: this.artifacts.concat(data)}); + }) + } + } + } + fetch(limit, maxage, callback) { + var data; + var req = new XMLHttpRequest(); + this.isLoading = true; + req.open('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`, true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if (req.status == 200) { + data = JSON.parse(req.responseText); + // -1 to be sure to not get the last item infinitely + if (data.length) { + this.maxage = data[data.length - 1].updated - 1; + callback(data); + } + } else { + alert("Erreur pendant le chargement de la page.\n"); + } + this.isLoading = false; + } + }; + req.send(null); + } +} +// Register custom element definition using standard platform API +customElements.define(ShellView.is, ShellView); \ No newline at end of file diff --git a/front/src/utils/eventSource.util.js b/front/src/utils/eventSource.util.js deleted file mode 100644 index c7d82e0..0000000 --- a/front/src/utils/eventSource.util.js +++ /dev/null @@ -1,77 +0,0 @@ -window.EventSourceManager = class EventSourceManager { - constructor(url, manager) { - //Create an Observable for the EventSource Connection - this.sourceObservable = Rx.Observable.create((observer) => { - this.source = new EventSource(url); - - const onOpen = (e) => { - observer.onNext(e); - console.log("%c SSE reconnected", "font-size:22px;text-shadow: 0 0 3px #FF0000, 0 0 5px blue;"); - - // this.source.removeEventListener('open', onOpen, false); - }; - - //TODO : On error subscribe to a new stream (wso ) - const onError = (e) => { - if (e.eventPhase === EventSource.CLOSED) { - observer.onCompleted(); - } else { - observer.onError(e); - } - }; - - const onMessage = (e) => { - observer.onNext(e); - }; - - this.source.addEventListener('open', onOpen, false); - this.source.addEventListener('error', onError, false); - this.source.addEventListener('message', onMessage, false); - - //Add listener on specific eventType - this.addEventsObservables(manager); - - return () => { - this.source.removeEventListener('error', onError, false); - this.source.removeEventListener('message', onMessage, false); - this.source.close(); - }; - }); - } - - static get source() { - return this.source; - } - - static get sourceObservable() { - return this.sourceObservable; - } - - /** - * Create an observable for each property of the given manager - * @param {Object} manager - * @returns {void} - */ - addEventsObservables(manager) { - //for each property - for (let eventType in manager) { - if (manager.hasOwnProperty(eventType)) { - - //Create a new observable - const observable = Rx.Observable.create((observer) => { - const onMessage = (e) => { - observer.onNext(e); - }; - - //listen the eventType on the EventSource - this.source.addEventListener(eventType, onMessage, false); - }); - - //Subscribe the callback. - observable.subscribe((e) => { - manager[eventType](e); - }); - } - } - } -}; \ No newline at end of file From 02969d4b83fe8c5719fcbe2c933362b9505c41dc Mon Sep 17 00:00:00 2001 From: nathandm Date: Mon, 9 Jan 2017 17:31:17 +0100 Subject: [PATCH 040/132] front: infinit scroll loader --- front/src/components/app-artifact-list.html | 76 +++++++++++++++++++-- front/src/shell/navbar/navbar.js | 8 +-- front/src/shell/pages/view/view.html | 8 ++- front/src/shell/pages/view/view.js | 13 ++-- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/front/src/components/app-artifact-list.html b/front/src/components/app-artifact-list.html index 83de92e..1034efb 100644 --- a/front/src/components/app-artifact-list.html +++ b/front/src/components/app-artifact-list.html @@ -29,11 +29,65 @@ color: #555555; margin:0 5px 0 0; } + + /*Loader*/ + .loader{ + list-style-type: none; + float: left; + } + .loader li { + display: inline-block; + height: .5em; + width: .5em; + margin-bottom: .7em; + background: white; + border-radius: 50%; + transition: opacity 0.22222s ease; + animation: bbbounce 2s infinite ease; + } + .loader li:nth-of-type(1){ + animation-delay: 0.22222s; + } + .loader li:nth-of-type(2) { + animation-delay: 0.44444s; + } + .loader li:nth-of-type(3) { + animation-delay: 0.66667s; + } + + @keyframes bbbounce { + 0%,100% { + opacity: 1; + transform: translateY(0em) scale(1); + } + 50% { + opacity: .8; + transform: translateY(1em) scale(0.8); + } + } + -
                    -

                    {{description}}

                    -

                    {{assignee}},

                    {{priority}},

                    {{category}}

                    -
                    + + + + + + + + diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js index 4d87943..950a136 100644 --- a/front/src/shell/pages/view/view.js +++ b/front/src/shell/pages/view/view.js @@ -14,20 +14,21 @@ class ShellView extends GlobalConst(Polymer.Element) { super.connectedCallback(); //throttle : function that only invokes func at most once per every wait milliseconds (350) - this._scroll= _.throttle(this.scroll.bind(this), 350); - this.$.content.addEventListener('scroll', this._scroll); + this._scrollListener= _.throttle(this._handleScroll.bind(this), 350); + this.$.content.addEventListener('scroll', this._scrollListener); if(this.id) { this.fetch(this.limit, -1, (data) => { - this.setProperties({artifacts: data}); + this.setProperties({artifacts: data, isLoading: false}); }) } } - scroll(e) { + _handleScroll() { if(this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { if (!this.isLoading && this.maxage ) { + this.setProperties({isLoading : true}); this.fetch(100, this.maxage, (data) => { - this.setProperties({artifacts: this.artifacts.concat(data)}); + this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); }) } } @@ -35,7 +36,6 @@ class ShellView extends GlobalConst(Polymer.Element) { fetch(limit, maxage, callback) { var data; var req = new XMLHttpRequest(); - this.isLoading = true; req.open('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`, true); req.onreadystatechange = () => { if (req.readyState == 4) { @@ -49,7 +49,6 @@ class ShellView extends GlobalConst(Polymer.Element) { } else { alert("Erreur pendant le chargement de la page.\n"); } - this.isLoading = false; } }; req.send(null); From ed29e3f044bfe8c54fc31b9c22fa7e8dcfeffd72 Mon Sep 17 00:00:00 2001 From: nathandm Date: Tue, 10 Jan 2017 14:05:22 +0100 Subject: [PATCH 041/132] front: create MixinBuilder + http.mixin + globalConst.mixin - manage subclass with mixin - create a http(XMLHttpRequest) mixin - externalise global constant un a dedicated mixin Signed-off-by: nathandm --- front/.eslintrc | 16 +++--- front/bower.json | 3 +- front/package.json | 2 + front/src/components/app-artifact-list.html | 21 ++++--- .../src/{constants => mixins}/const.mixin.js | 6 +- front/src/mixins/http.mixin.js | 27 +++++++++ front/src/mixins/mixinBuilder.js | 18 ++++++ front/src/reactivity-shell.html | 37 ++++++++++--- front/src/shell/navbar/navbar.js | 6 +- .../shell/pages/organisation/organisation.js | 29 ++++------ .../pages/organisations/organisations.js | 20 ++----- front/src/shell/pages/settings/settings.js | 2 +- front/src/shell/pages/view/view.js | 55 ++++++++----------- 13 files changed, 144 insertions(+), 98 deletions(-) rename front/src/{constants => mixins}/const.mixin.js (51%) create mode 100644 front/src/mixins/http.mixin.js create mode 100644 front/src/mixins/mixinBuilder.js diff --git a/front/.eslintrc b/front/.eslintrc index 1cc8ff1..e74ecc6 100644 --- a/front/.eslintrc +++ b/front/.eslintrc @@ -6,22 +6,22 @@ "node": true, "es6": true }, - "extends": "airbnb", // supposedly good, ajusting some values below. + "extends": "google", // supposedly good, ajusting some values below. "rules": { "arrow-body-style": ["error", "as-needed"], - "jsx-closing-bracket-location": 0, // doesn't always improve readability - "react/jsx-space-before-closing" : 0, - "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], "comma-dangle": 0, // too much "no-console": 0, "spaced-comment": 0, // sucks for code comments - "max-len": ["error", 160], // I don't code on my smartphone, need more than the default 80 - "no-param-reassign": ["error", { "props": false }] + "max-len": ["error", 120], // I don't code on my smartphone, need more than the default 80 + "no-param-reassign": ["error", { "props": false }], + "require-jsdoc": 0, + "new-cap": 0,//Mixin don't need new + "no-unused-vars": 0 //Mixin have global def }, "settings": { "import/resolve": { - "extensions": [ ".js", ".jsx" ] + "extensions": [ ".js" ] }, "import/parser": "babel-eslint" } -} \ No newline at end of file +} diff --git a/front/bower.json b/front/bower.json index 988c576..7a2824a 100644 --- a/front/bower.json +++ b/front/bower.json @@ -14,7 +14,8 @@ "paper-checkbox": "2.0-preview", "paper-icon-button": "2.0-preview", "app-router": "blasten/app-router#master", - "lodash": "^4.17.4" + "lodash": "^4.17.4", + "q": "^1.4.1" }, "resolutions": { "polymer": "2.0-preview", diff --git a/front/package.json b/front/package.json index b872599..8d1ec30 100644 --- a/front/package.json +++ b/front/package.json @@ -13,6 +13,8 @@ "bower": "^1.8.0", "browser-sync": "^2.18.5", "connect-history-api-fallback": "^1.3.0", + "eslint": "^3.13.1", + "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", "http-proxy-middleware": "^0.17.3" } diff --git a/front/src/components/app-artifact-list.html b/front/src/components/app-artifact-list.html index 1034efb..f650d23 100644 --- a/front/src/components/app-artifact-list.html +++ b/front/src/components/app-artifact-list.html @@ -16,25 +16,29 @@ padding: 0 10px; box-sizing: border-box; } + .description { font-size: 16px; color: white; - margin:0; + margin: 0; } + .sub { display: flex; } + .sub p { font-size: 14px; color: #555555; - margin:0 5px 0 0; + margin: 0 5px 0 0; } /*Loader*/ - .loader{ + .loader { list-style-type: none; float: left; } + .loader li { display: inline-block; height: .5em; @@ -45,18 +49,21 @@ transition: opacity 0.22222s ease; animation: bbbounce 2s infinite ease; } - .loader li:nth-of-type(1){ + + .loader li:nth-of-type(1) { animation-delay: 0.22222s; } + .loader li:nth-of-type(2) { animation-delay: 0.44444s; } + .loader li:nth-of-type(3) { animation-delay: 0.66667s; } @keyframes bbbounce { - 0%,100% { + 0%, 100% { opacity: 1; transform: translateY(0em) scale(1); } @@ -87,7 +94,6 @@ - - + + + +
                    diff --git a/front/src/shell/navbar/navbar.js b/front/src/shell/navbar/navbar.js index 365aadb..7e2226b 100644 --- a/front/src/shell/navbar/navbar.js +++ b/front/src/shell/navbar/navbar.js @@ -3,11 +3,11 @@ class ShellNavbar extends Polymer.Element { return 's-navbar'; } - handleClick (e) { + handleClick(e) { e.preventDefault(); - document.dispatchEvent(new CustomEvent('nav',{detail: {path:e.currentTarget.pathname}})); + document.dispatchEvent(new CustomEvent('nav', {detail: {path: e.currentTarget.pathname}})); } } // Register custom element definition using standard platform API -customElements.define(ShellNavbar.is, ShellNavbar); \ No newline at end of file +customElements.define(ShellNavbar.is, ShellNavbar); diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js index 57b5857..b39f67b 100644 --- a/front/src/shell/pages/organisation/organisation.js +++ b/front/src/shell/pages/organisation/organisation.js @@ -1,33 +1,24 @@ -class ShellOrganisation extends GlobalConst(Polymer.Element) { +class ShellOrganisation extends mix(Polymer.Element).with(GlobalConst, Http) { static get is() { return 's-organisation'; } - constructor (props) { + + constructor(props) { super(props); this.views = []; this.artifacts = []; } + connectedCallback() { super.connectedCallback(); - if(this.id) { - var req = new XMLHttpRequest(); - req.open('GET', `${this.wsURL}/subscribe/${this.id}`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if (req.status == 200) { - this.data = JSON.parse(req.responseText); - var groups = _.groupBy(this.data, "event"); - this.setProperties({views : groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); - - } else { - alert("Erreur pendant le chargement de la page.\n"); - } - } - }; - req.send(null); + if (this.id) { + this.fetch('GET', `${this.wsURL}/subscribe/${this.id}`).then((data) => { + const groups = _.groupBy(data, 'event'); + this.setProperties({views: groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); + }); } } } // Register custom element definition using standard platform API -customElements.define(ShellOrganisation.is, ShellOrganisation); \ No newline at end of file +customElements.define(ShellOrganisation.is, ShellOrganisation); diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js index 38e51db..71e3f37 100644 --- a/front/src/shell/pages/organisations/organisations.js +++ b/front/src/shell/pages/organisations/organisations.js @@ -1,4 +1,4 @@ -class ShellOrganisations extends GlobalConst(Polymer.Element) { +class ShellOrganisations extends mix(Polymer.Element).with(GlobalConst, Http) { static get is() { return 's-organisations'; } @@ -10,19 +10,11 @@ class ShellOrganisations extends GlobalConst(Polymer.Element) { connectedCallback() { super.connectedCallback(); - var req = new XMLHttpRequest(); - req.open('GET', `${this.wsURL}/load/organizations`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if(req.status == 200) { - this.organisations = JSON.parse(req.responseText); - } else { - alert("Don't be silly launch the server !\n"); - } - } - }; - req.send(null); + + this.fetch('GET', `${this.wsURL}/load/organizations`).then((data) => { + this.organisations = data; + }); } } // Register custom element definition using standard platform API -customElements.define(ShellOrganisations.is, ShellOrganisations); \ No newline at end of file +customElements.define(ShellOrganisations.is, ShellOrganisations); diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js index b60a0aa..2db4e23 100644 --- a/front/src/shell/pages/settings/settings.js +++ b/front/src/shell/pages/settings/settings.js @@ -3,4 +3,4 @@ class ShellSettings extends Polymer.Element { return 's-settings'; } } -customElements.define(ShellSettings.is, ShellSettings); \ No newline at end of file +customElements.define(ShellSettings.is, ShellSettings); diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js index 950a136..66250f1 100644 --- a/front/src/shell/pages/view/view.js +++ b/front/src/shell/pages/view/view.js @@ -1,8 +1,9 @@ -class ShellView extends GlobalConst(Polymer.Element) { +class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { static get is() { return 's-view'; } - constructor (props) { + + constructor(props) { super(props); this.limit = 50; this.artifacts = []; @@ -10,49 +11,37 @@ class ShellView extends GlobalConst(Polymer.Element) { this.isLoading = true; this.maxage = -1; } + connectedCallback() { super.connectedCallback(); - //throttle : function that only invokes func at most once per every wait milliseconds (350) - this._scrollListener= _.throttle(this._handleScroll.bind(this), 350); + //throttle:function that only invokes func at most once every (350) milli sec + this._scrollListener = _.throttle(this._handleScroll.bind(this), 350); this.$.content.addEventListener('scroll', this._scrollListener); - if(this.id) { - this.fetch(this.limit, -1, (data) => { - this.setProperties({artifacts: data, isLoading: false}); - }) + if (this.id) { + this.fetchNextData(this.limit, -1); } } + _handleScroll() { - if(this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { - if (!this.isLoading && this.maxage ) { - this.setProperties({isLoading : true}); - this.fetch(100, this.maxage, (data) => { - this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); - }) + if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { + if (!this.isLoading && this.maxage) { + this.setProperties({isLoading: true}); + this.fetchNextData(100, this.maxage); } } } - fetch(limit, maxage, callback) { - var data; - var req = new XMLHttpRequest(); - req.open('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if (req.status == 200) { - data = JSON.parse(req.responseText); - // -1 to be sure to not get the last item infinitely - if (data.length) { - this.maxage = data[data.length - 1].updated - 1; - callback(data); - } - } else { - alert("Erreur pendant le chargement de la page.\n"); + + fetchNextData(limit, maxage) { + this.fetch('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`) + .then((data) => { + if (data.length) { + this.maxage = data[data.length - 1].updated - 1; + this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); } - } - }; - req.send(null); + }); } } // Register custom element definition using standard platform API -customElements.define(ShellView.is, ShellView); \ No newline at end of file +customElements.define(ShellView.is, ShellView); From 34abec197e3874b67eaec32f0f2afb99469ec87b Mon Sep 17 00:00:00 2001 From: nathandm Date: Tue, 10 Jan 2017 15:34:17 +0100 Subject: [PATCH 042/132] front: navBar replace logo with previous history --- front/src/reactivity-shell.html | 9 --------- front/src/shell/navbar/idea.png | Bin 112692 -> 0 bytes front/src/shell/navbar/navbar.html | 23 +++++++++-------------- front/src/shell/navbar/navbar.js | 4 ++++ 4 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 front/src/shell/navbar/idea.png diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index adf9e05..118714b 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -50,15 +50,6 @@ return 's-reactivity'; } - static get config() { - return { - listeners: { - 'nav': '_onNav', - 'error': '_onError' - } - } - } - connectedCallback() { super.connectedCallback(); diff --git a/front/src/shell/navbar/idea.png b/front/src/shell/navbar/idea.png deleted file mode 100644 index e65b6587696a80aa728b2cb0a7b37cbb46ea2c50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112692 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%0A+70|6Jra*EdGmbSDu= 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�-|)VK`U%Fow=|W)Ua3@WH4Pw=&a} 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 + - - diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index 118714b..13c5d25 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -63,6 +63,11 @@ document.addEventListener('error', () => { this.$.router.go('/404'); }); + + document.addEventListener('changeColor', (e) => { + this.updateStyles({'--app-primary-color': e.detail.primary}); + this.updateStyles({'--app-secondary-color': e.detail.secondary}); + }); //No need to removeListener cause this is the main entry point of the application (if unmount no more fun) } } diff --git a/front/src/shell/pages/404/view-404.html b/front/src/shell/pages/404/view-404.html index 8cab5bc..5aea619 100644 --- a/front/src/shell/pages/404/view-404.html +++ b/front/src/shell/pages/404/view-404.html @@ -3,6 +3,11 @@ diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/pages/settings/settings.js index 2db4e23..4940fcc 100644 --- a/front/src/shell/pages/settings/settings.js +++ b/front/src/shell/pages/settings/settings.js @@ -2,5 +2,14 @@ class ShellSettings extends Polymer.Element { static get is() { return 's-settings'; } + + changeColor(e) { + document.dispatchEvent(new CustomEvent('changeColor', { + detail: { + primary: e.currentTarget.getAttribute('primary'), + secondary: e.currentTarget.getAttribute('secondary') + } + })); + } } customElements.define(ShellSettings.is, ShellSettings); diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js index 66250f1..ce15bc2 100644 --- a/front/src/shell/pages/view/view.js +++ b/front/src/shell/pages/view/view.js @@ -5,7 +5,9 @@ class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { constructor(props) { super(props); - this.limit = 50; + this.artifactLimit = 50; + //Limit in pixel before fetching others data + this.threshold = 1500; this.artifacts = []; //load on init this.isLoading = true; @@ -14,27 +16,23 @@ class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { connectedCallback() { super.connectedCallback(); - - //throttle:function that only invokes func at most once every (350) milli sec - this._scrollListener = _.throttle(this._handleScroll.bind(this), 350); - this.$.content.addEventListener('scroll', this._scrollListener); - if (this.id) { - this.fetchNextData(this.limit, -1); + this.$.content.addEventListener('scroll', this._handleScroll.bind(this)); + this.fetchNextData(this.artifactLimit, -1); } } _handleScroll() { - if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - 150) { + if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - this.threshold) { if (!this.isLoading && this.maxage) { this.setProperties({isLoading: true}); - this.fetchNextData(100, this.maxage); + this.fetchNextData(this.artifactLimit, this.maxage); } } } fetchNextData(limit, maxage) { - this.fetch('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${this.limit}/maxage/${maxage}`) + this.fetch('GET', `${this.wsURL}/load/artifacts/${this.id}/limit/${limit}/maxage/${maxage}`) .then((data) => { if (data.length) { this.maxage = data[data.length - 1].updated - 1; From f07b4f157f191e2cba30ac25f2dfb8bd1acf1f08 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 11:54:51 +0100 Subject: [PATCH 044/132] test: front test with travis --- .travis.yml | 35 ++++++++++++++++++++++++++++------- front/package.json | 3 ++- front/test/routertest.html | 19 +++++++++++++++++++ wct.conf.json | 18 ++++++++++++++++++ 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 front/test/routertest.html create mode 100644 wct.conf.json diff --git a/.travis.yml b/.travis.yml index 726734b..3a126d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,40 @@ language: java jdk: - - oraclejdk8 +- oraclejdk8 +dist: trusty +sudo: required +addons: + firefox: latest + sauce_connect: true + apt: + sources: + - google-chrome + packages: + - google-chrome-stable env: + matrix: - MODULE=core - MODULE=core/java-lib - MODULE=core/broadcaster + global: + - secure: jyieF62Bdxn8KzePW73qAWOsV6n83gIGDpKGpgbpbCDG+R5AcMqFSXf32A7So148YczZIjlhWUx85Vrol9NURpimxcFfZU9nKCPjZt7QpGuzXInbxy8Qrze1I/PN40nMVGxNEam7pwXIMZwbOFPv/PG+fDw7BCDSfsoTwTMbk1FvjhpBngo6DBtbyld30HEhIVSSLhROgcQ7zzcfHYdu9IxvnDFnuNict1m1+HGJf0jDAbFRzGteMq7/tu+rM9HHd5UyCSI2sGWo4P3tAcXw/l3VV/MxhG6cRofvQQEY7H+2JpJ5U6CDprrN7sOUrc777cmOzXzB1sYLaNNniQiScQtZJTYcJbhPF/rOYn8FjrFDMxC0EB7oQQ58Z4dUViKNAdR54zxFwm/OqDssAV43fAOFfkiZGfdKjR+CQQMWRD7UdaCfsy3w/xFw5KiNObyUtvbBJABVYWjsIIF0yU20Qu8Cr5DCfUjUTyNiWz/LjN3cQjtv6HB7A3UWc/ohjhU/GZoWvvJ5WySKLMuL8msH5/R3UE+u8TZYTi9pSG/k6BSoVO4DRAmvG/7FdepwHJYnW1Ol1Bd2u0OdqiwhByu0yDH9jolJ/VYBq8A880nU+dW5SVhwTNSvLgo3jclr4IwkCydZvZ6YjiqpKSqW69TRWyPLOFVLtn943yFX89OXhwY= + - secure: Y0Ck+5ZLdbv2qC5uySGZZmuU1++fBvLOtUquAiFn4IPXzaMTbURqr0jY6+R/ibiJdnTgKk4H65Gl4S2d2UPSVTeIUoxnzSSiUHhoxp1FUrGuK8wxuk0ZMffRIMWODRxLJsuFKXTt7NpwGmDx53Kzzp0cbMo9ilkVFJu+6PBxmTC2lRZwIeHFo4U56DjNwWaHA3dXwfiv1S6tsfem3kxGHb9SsfN4UGyXTRqKZBK+DwEs07Uwwq6FkCqsSzcV42uTx2t9qRrVbs3elIPdNKIitEE/V+juQTg94c3BHspXHZhgExByAm/2SSOZaE2syyrFGPSPRf1XKbzwkiGCJiCWzT8naOZB8CC6V4QxDwbrh6nU9lH2BQdcDZj9/pcT6HPc8nhoBKwl4Rzpnsva4b70cZE57KaRwiDQ4lUvFPJ3YepPiz0QKNnEPYHAbLFIMBvsVcMGGkKg4aMSiBfCXu62PTAAqUNQJKRVEIpYAs6AHE3IUUsh+gZ24yWddEHRE3mUvi3vIYnNUjlt0e9fTqfitl1wMV68CZb5llm19jLYFZ0uX54d0xkCtdmNWBvZBip4lVpuR/qQYgLp2LOiEqgzFNLg4x6DxRabUAZ0tP/daIQ2WBBTXKIkxCk2Am/xwFCyz9Bzb97DTSUjvdtI++bGd1nn6LN8AZ01RhO4I5Rff/0= + before_install: - - cp config/settings.xml ~/.m2/settings.xml - - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc -script: - - mvn -f $MODULE/pom.xml clean deploy +- cp config/settings.xml ~/.m2/settings.xml +- echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc +before_script: +- npm install -g bower polylint web-component-tester +- bower install +- polylint +script: +- mvn -f $MODULE/pom.xml clean deploy +- xvfb-run wct front/test --skip-plugin sauce +- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct front/test -s 'default' --skip-plugin local; fi deploy: - provider: cloudfoundry api: https://api.run.pivotal.io - username: $CF_DEPLOY_USERNAME + username: "$CF_DEPLOY_USERNAME" password: secure: RE8snuhO5HKpV8RUPpVXP7KTaqSJb3DS4FSIzaCMy/+N54Uqvnk3ecFyXGJpgYSCRmiZEohP9hXSGYaRX2aXOQvnxtnRcH73X0J8c5T1o3gvLKUdlyZg2HIEMUu7x2pVekJqe300hlhi5Uxgm2MLjdFD74xhoN7USKnBdXOF/cyjyIomEr4HX8UCxMxjbgeAyGXvPyrH1bQbp6j9QdZy06zpMD0+MYzl66qYV4pgxeey/jyqh+ldAQOtXqXueIGG3PQr+zFQTX9Jnw7ilKvP9HqTAngt32GaccB+zCmMhmKFouodJzKtFnpGOQ6d82XgdKMcZcZfGK9zMI1ZDVLWrftfjap+8TFuHpiJbghj52bGrMAD0/+d14LxsSVj0NyuQ7a/7RwalfF9aZOs1CNrLO9KkKy+5ncUKv6dLZtBZ9bN9+bwdb3pfS78C7JaJQOkv6jjXYIJv/yssBla90gfXJY+htnax+5Owss391JYsjhKFtbYL9LhEe587t5lsz7tkOygRPQ+zARInmJt6DBc/rHdtLDMOdhEaA+MKt9Sfn2TZuTXKsu3/pbM95+061u3wmZl7jxqjhnfMhHn1JkD0CkmWxbKLbL9ELy99TvySjdaT+qVar5AXSpL3iyLX/APjFz0RA+1jO8kOlfYebFZYNwoClJG7oTw9XcUuePGb7Q= organization: Reactivity @@ -22,4 +43,4 @@ deploy: on: repo: reactivity-io/reactivity branch: travis-deploy - condition: $MODULE = 'core/broadcaster' + condition: "$MODULE = 'core/broadcaster'" \ No newline at end of file diff --git a/front/package.json b/front/package.json index 8d1ec30..eabf110 100644 --- a/front/package.json +++ b/front/package.json @@ -16,6 +16,7 @@ "eslint": "^3.13.1", "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", - "http-proxy-middleware": "^0.17.3" + "http-proxy-middleware": "^0.17.3", + "web-component-tester": "^5.0.0" } } diff --git a/front/test/routertest.html b/front/test/routertest.html new file mode 100644 index 0000000..e3b090d --- /dev/null +++ b/front/test/routertest.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/wct.conf.json b/wct.conf.json new file mode 100644 index 0000000..ea4cbec --- /dev/null +++ b/wct.conf.json @@ -0,0 +1,18 @@ +{ + "verbose": false, + "plugins": { + "sauce": { + "disabled": true, + "browsers": [ + { + "browserName": "chrome", + "platform": "Windows 10", + "version": "55" + } + ] + }, + "local": { + "browsers": ["chrome"] + } + } +} \ No newline at end of file From 40a8b06541c53e76bf2d2e22fb9b52c103a9a325 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 13:58:18 +0100 Subject: [PATCH 045/132] travis: launch test with travis Signed-off-by: nathandm --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3a126d5..3ed3725 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,9 +24,11 @@ before_install: - cp config/settings.xml ~/.m2/settings.xml - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc before_script: +- cd front - npm install -g bower polylint web-component-tester - bower install - polylint +- cd .. script: - mvn -f $MODULE/pom.xml clean deploy - xvfb-run wct front/test --skip-plugin sauce From 5c696e3e1a124585da14b078c9d3bd8ecc3c7653 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 13:58:57 +0100 Subject: [PATCH 046/132] front: http mixin + change couchbase db --- .../broadcaster/config/CouchbaseConfig.java | 2 +- .../core/broadcaster/RepositoryTest.java | 2 +- front/gulpfile.js | 1 - front/src/mixins/http.mixin.js | 24 ++++++++++++++++--- front/src/reactivity-shell.html | 3 +-- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 6465318..9ecc9bf 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -50,7 +50,7 @@ public class CouchbaseConfig { @Bean Bucket sync() { final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); - final Bucket bucket = cluster.openBucket("default"); + final Bucket bucket = cluster.openBucket("nana"); bucket.bucketManager().createN1qlPrimaryIndex(true, false); return bucket; diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java index 6809d32..f1f0e7a 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -81,6 +81,6 @@ public void insertSomeArtifactsDocuments() { .put("categories", JsonObject.from(categories)); return bucket.insert(JsonDocument.create(id, object)); - }).repeat(1000).subscribe(); + }).repeat(100).subscribe(); } } diff --git a/front/gulpfile.js b/front/gulpfile.js index b153f6a..12128a6 100644 --- a/front/gulpfile.js +++ b/front/gulpfile.js @@ -25,4 +25,3 @@ gulp.task('default', () => { } }); }); - diff --git a/front/src/mixins/http.mixin.js b/front/src/mixins/http.mixin.js index c2c1974..d153108 100644 --- a/front/src/mixins/http.mixin.js +++ b/front/src/mixins/http.mixin.js @@ -12,11 +12,11 @@ let Http = (superclass) => class extends superclass { deferred.resolve(retval); } catch (e) { deferred.reject(e.message); - document.dispatchEvent(new CustomEvent('error', {detail: {message: e.message}})); + this.dispatchCustomError(e.message); } } else { - deferred.reject(req.error); - document.dispatchEvent(new CustomEvent('error')); + deferred.reject(req.responseText); + this.dispatchRequestError(req); } } }; @@ -24,4 +24,22 @@ let Http = (superclass) => class extends superclass { return deferred.promise; } + + dispatchRequestError(req) { + document.dispatchEvent(new CustomEvent('error', { + detail: { + status: req.status, + statusText: req.statusText, + message: req.responseText + } + })); + } + + dispatchCustomError(message) { + document.dispatchEvent(new CustomEvent('error', { + detail: { + message: message + } + })); + } }; diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index 13c5d25..0029f2b 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -28,7 +28,7 @@ } - + - - + + From 1c56caede6e221ec1efae3cf81997e4f836d45e7 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 15:56:37 +0100 Subject: [PATCH 054/132] travis: change dep web-component-tester from bower --- front/bower.json | 4 ++++ front/package.json | 3 +-- front/test/routertest.html | 3 +-- wct.conf.json | 7 +------ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/front/bower.json b/front/bower.json index 7a2824a..c42299f 100644 --- a/front/bower.json +++ b/front/bower.json @@ -17,7 +17,11 @@ "lodash": "^4.17.4", "q": "^1.4.1" }, + "devDependencies": { + "web-component-tester": "^4.0.0" + }, "resolutions": { + "lodash": "^3.7.0", "polymer": "2.0-preview", "webcomponentsjs": "v1" } diff --git a/front/package.json b/front/package.json index eabf110..8d1ec30 100644 --- a/front/package.json +++ b/front/package.json @@ -16,7 +16,6 @@ "eslint": "^3.13.1", "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", - "http-proxy-middleware": "^0.17.3", - "web-component-tester": "^5.0.0" + "http-proxy-middleware": "^0.17.3" } } diff --git a/front/test/routertest.html b/front/test/routertest.html index 962b71a..c67bd33 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -2,8 +2,7 @@ - - + diff --git a/wct.conf.json b/wct.conf.json index aba3bcd..3f5054c 100644 --- a/wct.conf.json +++ b/wct.conf.json @@ -5,14 +5,9 @@ "disabled": true, "browsers": [ { - "browserName": "microsoftedge", + "browserName": "chrome", "platform": "Windows 10", "version": "" - }, - { - "browserName": "internet explorer", - "platform": "Windows 8.1", - "version": "11" } ] }, From 02b9d0c40a4e6f6957cec4e66bbe2eb7541a6d78 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 16:15:07 +0100 Subject: [PATCH 055/132] travis: more test :) --- front/test/awesome-tests.js | 8 ++++++++ front/test/routertest.html | 12 +++++------- 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 front/test/awesome-tests.js diff --git a/front/test/awesome-tests.js b/front/test/awesome-tests.js new file mode 100644 index 0000000..ed1aff9 --- /dev/null +++ b/front/test/awesome-tests.js @@ -0,0 +1,8 @@ +suite('', function () { + test('SHOULD BE TRUUUUe', function () { + assert.isNotNull({toto: 'toto'}); + }); + test('have navbar', function () { + assert.isNotNull(document.querySelector('s-reactivity').shadowRoot.querySelector('s-navbar')); + }); +}); \ No newline at end of file diff --git a/front/test/routertest.html b/front/test/routertest.html index c67bd33..716e933 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -2,17 +2,15 @@ - - + \ No newline at end of file From b9ffdee7cd95fa253f513ce9b35567d4a1f3ba50 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 11 Jan 2017 17:24:35 +0100 Subject: [PATCH 056/132] travis: fix on local test on travis --- .travis.yml | 2 +- front/package.json | 3 ++- front/test/awesome-tests.js | 8 -------- front/test/routertest.html | 17 +++++++++++------ 4 files changed, 14 insertions(+), 16 deletions(-) delete mode 100644 front/test/awesome-tests.js diff --git a/.travis.yml b/.travis.yml index 0ff7817..9c7a0d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ before_script: - cd .. script: - mvn -f $MODULE/pom.xml clean deploy -- xvfb-run wct front/ --plugin sauce --skip-plugin local +- xvfb-run wct front/test --plugin sauce --skip-plugin local deploy: - provider: cloudfoundry api: https://api.run.pivotal.io diff --git a/front/package.json b/front/package.json index 8d1ec30..142ffa1 100644 --- a/front/package.json +++ b/front/package.json @@ -16,6 +16,7 @@ "eslint": "^3.13.1", "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", - "http-proxy-middleware": "^0.17.3" + "http-proxy-middleware": "^0.17.3", + "webdriver-manager": "^11.1.1" } } diff --git a/front/test/awesome-tests.js b/front/test/awesome-tests.js deleted file mode 100644 index ed1aff9..0000000 --- a/front/test/awesome-tests.js +++ /dev/null @@ -1,8 +0,0 @@ -suite('', function () { - test('SHOULD BE TRUUUUe', function () { - assert.isNotNull({toto: 'toto'}); - }); - test('have navbar', function () { - assert.isNotNull(document.querySelector('s-reactivity').shadowRoot.querySelector('s-navbar')); - }); -}); \ No newline at end of file diff --git a/front/test/routertest.html b/front/test/routertest.html index 716e933..eadba9e 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -2,15 +2,20 @@ - + + - + \ No newline at end of file From 6f647d43ba7d5a276be61868087229b04339731f Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 09:57:11 +0100 Subject: [PATCH 057/132] travis: add travis icon on readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c16275b..7c888d5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Reactivity +## Reactivity [![Build Status](https://travis-ci.org/reactivity-io/reactivity.svg?branch=master)](https://travis-ci.org/reactivity-io/reactivity) Reactivity is an open-source WEB application offering a visual way to manage your activity in a reactive manner. From fbf9c87a324d45952c5f1ec13ea105b8a881f317 Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 10:33:08 +0100 Subject: [PATCH 058/132] travis: slack integration --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9c7a0d1..ee05c33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,4 +43,6 @@ deploy: on: repo: reactivity-io/reactivity branch: travis-deploy - condition: "$MODULE = 'core/broadcaster'" \ No newline at end of file + condition: "$MODULE = 'core/broadcaster'" +notifications: + slack: reactivity:eHHORgWgihkLbNPLR4B7RAOv \ No newline at end of file From 60aef85241622943d4f738d7f797285c56b71da4 Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 11:14:54 +0100 Subject: [PATCH 059/132] couchbase: remove perso cfg --- .../broadcaster/config/CouchbaseConfig.java | 51 +++++-- front/src/components/app-orga.html | 143 ++++++++++++++++++ 2 files changed, 182 insertions(+), 12 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 9ecc9bf..62ce77a 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -22,6 +22,13 @@ * This configuration initializes couchbase connection. *

                      * + *

                      + * See the class constants for properties that can be configured and their default values. Node that properties can be + * configured in several ways thanks to Spring Boot property resolution. For instance, you can run the application with + * program argument {@code --reactivity.couchbase.nodes=xxx} to define the {@code reactivity.couchbase.nodes} property. + * More details here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html + *

                      + * * @author Guillaume DROUET * @since 0.1.0 */ @@ -29,8 +36,25 @@ @ConfigurationProperties(prefix = "reactivity.couchbase") public class CouchbaseConfig { -// private String[] nodes = new String[] { "127.0.0.1" } ; - private String[] nodes = new String[] { "ec2-35-160-191-149.us-west-2.compute.amazonaws.com" } ; + /** + * Default value for property {@code reactivity.couchbase.nodes}. + */ + public static final String DEFAULT_COUCHBASE_NODES = "127.0.0.1"; + + /** + * Default value for property {@code reactivity.couchbase.bucket}. + */ + public static final String DEFAULT_COUCHBASE_BUCKET = "artifact"; + + /** + * Nodes to use. See {@link #DEFAULT_COUCHBASE_NODES default} value. + */ + private String[] nodes = new String[] { DEFAULT_COUCHBASE_NODES } ; + + /** + * Bucket name. See {@link #DEFAULT_COUCHBASE_BUCKET default} value. + */ + private String bucket = DEFAULT_COUCHBASE_BUCKET; /** *

                      @@ -39,18 +63,10 @@ public class CouchbaseConfig { * * @return the {@code Bucket} */ -// @Bean -// Bucket sync() { -// final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); -// final Bucket bucket = cluster.openBucket("artifact"); -// bucket.bucketManager().createN1qlPrimaryIndex(true, false); -// -// return bucket; -// } @Bean Bucket sync() { final CouchbaseCluster cluster = CouchbaseCluster.create(nodes); - final Bucket bucket = cluster.openBucket("nana"); + final Bucket bucket = cluster.openBucket(this.bucket); bucket.bucketManager().createN1qlPrimaryIndex(true, false); return bucket; @@ -66,4 +82,15 @@ Bucket sync() { public void setNodes(final String[] nodes) { this.nodes = nodes; } -} + + /** + *

                      + * Sets the bucket where data are managed. + *

                      + * + * @param bucket the bucket name + */ + public void setBucket(final String bucket) { + this.bucket = bucket; + } +} \ No newline at end of file diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html index e69de29..6019267 100644 --- a/front/src/components/app-orga.html +++ b/front/src/components/app-orga.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + From 075066bad3d4fc23ccbbe065116e68e2e85069bb Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 11:23:18 +0100 Subject: [PATCH 060/132] pom: update pom with gium change --- core/broadcaster/pom.xml | 54 ++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 1b187f5..67678cf 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -82,26 +82,12 @@ ${project.version} - + io.projectreactor reactor-core ${reactor-core.version} - - - org.springframework.boot.experimental - spring-boot-starter-web-reactive - - org.springframework.boot @@ -143,6 +129,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring-boot-dependencies.version} @@ -154,6 +141,41 @@ + + + + netty + + true + + + + io.projectreactor.ipc + reactor-netty + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + + tomcat + + + org.springframework.boot.experimental + spring-boot-starter-web-reactive + + + + + @@ -208,4 +230,4 @@ - + \ No newline at end of file From 089165a0d4e0d263d0ca432d6265d3dad0efc73c Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 14:57:13 +0100 Subject: [PATCH 061/132] travis: module configuration for test --- .travis.yml | 8 +- .../broadcaster/config/CouchbaseConfig.java | 2 +- front/src/utils/eventSource.util.js | 77 ------------------- 3 files changed, 3 insertions(+), 84 deletions(-) delete mode 100644 front/src/utils/eventSource.util.js diff --git a/.travis.yml b/.travis.yml index ee05c33..ae9021f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,18 +19,14 @@ env: global: - secure: jyieF62Bdxn8KzePW73qAWOsV6n83gIGDpKGpgbpbCDG+R5AcMqFSXf32A7So148YczZIjlhWUx85Vrol9NURpimxcFfZU9nKCPjZt7QpGuzXInbxy8Qrze1I/PN40nMVGxNEam7pwXIMZwbOFPv/PG+fDw7BCDSfsoTwTMbk1FvjhpBngo6DBtbyld30HEhIVSSLhROgcQ7zzcfHYdu9IxvnDFnuNict1m1+HGJf0jDAbFRzGteMq7/tu+rM9HHd5UyCSI2sGWo4P3tAcXw/l3VV/MxhG6cRofvQQEY7H+2JpJ5U6CDprrN7sOUrc777cmOzXzB1sYLaNNniQiScQtZJTYcJbhPF/rOYn8FjrFDMxC0EB7oQQ58Z4dUViKNAdR54zxFwm/OqDssAV43fAOFfkiZGfdKjR+CQQMWRD7UdaCfsy3w/xFw5KiNObyUtvbBJABVYWjsIIF0yU20Qu8Cr5DCfUjUTyNiWz/LjN3cQjtv6HB7A3UWc/ohjhU/GZoWvvJ5WySKLMuL8msH5/R3UE+u8TZYTi9pSG/k6BSoVO4DRAmvG/7FdepwHJYnW1Ol1Bd2u0OdqiwhByu0yDH9jolJ/VYBq8A880nU+dW5SVhwTNSvLgo3jclr4IwkCydZvZ6YjiqpKSqW69TRWyPLOFVLtn943yFX89OXhwY= - secure: Y0Ck+5ZLdbv2qC5uySGZZmuU1++fBvLOtUquAiFn4IPXzaMTbURqr0jY6+R/ibiJdnTgKk4H65Gl4S2d2UPSVTeIUoxnzSSiUHhoxp1FUrGuK8wxuk0ZMffRIMWODRxLJsuFKXTt7NpwGmDx53Kzzp0cbMo9ilkVFJu+6PBxmTC2lRZwIeHFo4U56DjNwWaHA3dXwfiv1S6tsfem3kxGHb9SsfN4UGyXTRqKZBK+DwEs07Uwwq6FkCqsSzcV42uTx2t9qRrVbs3elIPdNKIitEE/V+juQTg94c3BHspXHZhgExByAm/2SSOZaE2syyrFGPSPRf1XKbzwkiGCJiCWzT8naOZB8CC6V4QxDwbrh6nU9lH2BQdcDZj9/pcT6HPc8nhoBKwl4Rzpnsva4b70cZE57KaRwiDQ4lUvFPJ3YepPiz0QKNnEPYHAbLFIMBvsVcMGGkKg4aMSiBfCXu62PTAAqUNQJKRVEIpYAs6AHE3IUUsh+gZ24yWddEHRE3mUvi3vIYnNUjlt0e9fTqfitl1wMV68CZb5llm19jLYFZ0uX54d0xkCtdmNWBvZBip4lVpuR/qQYgLp2LOiEqgzFNLg4x6DxRabUAZ0tP/daIQ2WBBTXKIkxCk2Am/xwFCyz9Bzb97DTSUjvdtI++bGd1nn6LN8AZ01RhO4I5Rff/0= - before_install: - cp config/settings.xml ~/.m2/settings.xml - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc before_script: -- cd front -- npm install -- npm install -g web-component-tester -- cd .. +- if [ "$MODULE" = "core/broadcaster" ]; then npm install -g web-component-tester; fi script: - mvn -f $MODULE/pom.xml clean deploy -- xvfb-run wct front/test --plugin sauce --skip-plugin local +- if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run wct front/test --plugin sauce --skip-plugin local; fi deploy: - provider: cloudfoundry api: https://api.run.pivotal.io diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 1490e09..62ce77a 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -93,4 +93,4 @@ public void setNodes(final String[] nodes) { public void setBucket(final String bucket) { this.bucket = bucket; } -} +} \ No newline at end of file diff --git a/front/src/utils/eventSource.util.js b/front/src/utils/eventSource.util.js deleted file mode 100644 index c7d82e0..0000000 --- a/front/src/utils/eventSource.util.js +++ /dev/null @@ -1,77 +0,0 @@ -window.EventSourceManager = class EventSourceManager { - constructor(url, manager) { - //Create an Observable for the EventSource Connection - this.sourceObservable = Rx.Observable.create((observer) => { - this.source = new EventSource(url); - - const onOpen = (e) => { - observer.onNext(e); - console.log("%c SSE reconnected", "font-size:22px;text-shadow: 0 0 3px #FF0000, 0 0 5px blue;"); - - // this.source.removeEventListener('open', onOpen, false); - }; - - //TODO : On error subscribe to a new stream (wso ) - const onError = (e) => { - if (e.eventPhase === EventSource.CLOSED) { - observer.onCompleted(); - } else { - observer.onError(e); - } - }; - - const onMessage = (e) => { - observer.onNext(e); - }; - - this.source.addEventListener('open', onOpen, false); - this.source.addEventListener('error', onError, false); - this.source.addEventListener('message', onMessage, false); - - //Add listener on specific eventType - this.addEventsObservables(manager); - - return () => { - this.source.removeEventListener('error', onError, false); - this.source.removeEventListener('message', onMessage, false); - this.source.close(); - }; - }); - } - - static get source() { - return this.source; - } - - static get sourceObservable() { - return this.sourceObservable; - } - - /** - * Create an observable for each property of the given manager - * @param {Object} manager - * @returns {void} - */ - addEventsObservables(manager) { - //for each property - for (let eventType in manager) { - if (manager.hasOwnProperty(eventType)) { - - //Create a new observable - const observable = Rx.Observable.create((observer) => { - const onMessage = (e) => { - observer.onNext(e); - }; - - //listen the eventType on the EventSource - this.source.addEventListener(eventType, onMessage, false); - }); - - //Subscribe the callback. - observable.subscribe((e) => { - manager[eventType](e); - }); - } - } - } -}; \ No newline at end of file From 3cbfe47492dd0bd61ab6fff0375f8c803ad38aa2 Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 12 Jan 2017 16:29:46 +0100 Subject: [PATCH 062/132] e2e: init e2e & test on spring confproperties --- .../core/broadcaster/config/CouchbaseConfig.java | 6 +++--- front/e2e/conf.js | 6 ++++++ front/e2e/spec.js | 9 +++++++++ front/package.json | 4 ++++ front/test/routertest.html | 3 --- 5 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 front/e2e/conf.js create mode 100644 front/e2e/spec.js diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java index 62ce77a..420c0ba 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/config/CouchbaseConfig.java @@ -44,7 +44,7 @@ public class CouchbaseConfig { /** * Default value for property {@code reactivity.couchbase.bucket}. */ - public static final String DEFAULT_COUCHBASE_BUCKET = "artifact"; + public static final String BUCKET = "artifact"; /** * Nodes to use. See {@link #DEFAULT_COUCHBASE_NODES default} value. @@ -52,9 +52,9 @@ public class CouchbaseConfig { private String[] nodes = new String[] { DEFAULT_COUCHBASE_NODES } ; /** - * Bucket name. See {@link #DEFAULT_COUCHBASE_BUCKET default} value. + * Bucket name. See {@link #BUCKET default} value. */ - private String bucket = DEFAULT_COUCHBASE_BUCKET; + private String bucket = BUCKET; /** *

                      diff --git a/front/e2e/conf.js b/front/e2e/conf.js new file mode 100644 index 0000000..db10309 --- /dev/null +++ b/front/e2e/conf.js @@ -0,0 +1,6 @@ +// conf.js +exports.config = { + framework: 'jasmine', + seleniumAddress: 'http://localhost:4444/wd/hub', + specs: ['spec.js'] +}; diff --git a/front/e2e/spec.js b/front/e2e/spec.js new file mode 100644 index 0000000..2cdefd2 --- /dev/null +++ b/front/e2e/spec.js @@ -0,0 +1,9 @@ +// spec.js +describe('reactivity app', function() { + it('should have a title', function() { + browser.ignoreSynchronization = true; + browser.get('http://localhost:3000/'); + + expect(browser.getTitle()).toEqual('Reactivity'); + }); +}); \ No newline at end of file diff --git a/front/package.json b/front/package.json index 142ffa1..57b68e7 100644 --- a/front/package.json +++ b/front/package.json @@ -6,6 +6,9 @@ "scripts": { "postinstall": "bower install", "start": "gulp", + "webdriverupdate": "webdriver-manager update", + "webdriverstart": "webdriver-manager start", + "e2e": "protractor e2e/conf.js", "lint": "./node_modules/.bin/eslint ." }, "author": "Nathan DAMIE", @@ -17,6 +20,7 @@ "eslint-config-google": "^0.7.1", "gulp": "github:gulpjs/gulp#4.0", "http-proxy-middleware": "^0.17.3", + "protractor": "^5.0.0", "webdriver-manager": "^11.1.1" } } diff --git a/front/test/routertest.html b/front/test/routertest.html index eadba9e..92837b6 100644 --- a/front/test/routertest.html +++ b/front/test/routertest.html @@ -9,9 +9,6 @@ - - - From 7437d38f531bb439f6b23307a57abf0f1d6ef813 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 10:35:29 +0100 Subject: [PATCH 068/132] travis: update node version to support --- .travis.yml | 2 ++ front/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 55b4367..77c6344 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: java jdk: - oraclejdk8 +node_js: +- "6.1" dist: trusty sudo: required addons: diff --git a/front/package.json b/front/package.json index 7bd0c3f..67b79cc 100644 --- a/front/package.json +++ b/front/package.json @@ -41,6 +41,6 @@ "http-proxy-middleware": "^0.17.3", "protractor": "^5.0.0", "web-component-tester": "^5.0.0", - "webdriver-manager": "^11.1.1" + "webdriver-manager": "10.2.8" } } \ No newline at end of file From 68ed9fa9b2f54acd154e1d8538eacbf168334743 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 10:56:34 +0100 Subject: [PATCH 069/132] travis: node 6.1 ! --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 77c6344..8f3ab1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: java jdk: - oraclejdk8 -node_js: -- "6.1" +node_js: "6.1" dist: trusty sudo: required addons: From e0ff01313961e8aeb6e42cb336e3a5db5dddfd4c Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 11:20:45 +0100 Subject: [PATCH 070/132] travis: update nvm ? --- .travis.yml | 4 +++- front/package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f3ab1a..2b3c559 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: java jdk: - oraclejdk8 -node_js: "6.1" +node_js: +- "6" dist: trusty sudo: required addons: @@ -27,6 +28,7 @@ script: - mvn -f $MODULE/pom.xml clean deploy - 'if [ "$MODULE" = "core/broadcaster" ]; then cd front; + nvm install node; npm install; xvfb-run npm test; xvfb-run npm run e2e; diff --git a/front/package.json b/front/package.json index 67b79cc..7bd0c3f 100644 --- a/front/package.json +++ b/front/package.json @@ -41,6 +41,6 @@ "http-proxy-middleware": "^0.17.3", "protractor": "^5.0.0", "web-component-tester": "^5.0.0", - "webdriver-manager": "10.2.8" + "webdriver-manager": "^11.1.1" } } \ No newline at end of file From 8ac5af44a5a3e01dedc944be0ffdd2a6143bebac Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 14:13:34 +0100 Subject: [PATCH 071/132] travis: start webdriver with dedicated conf --- front/e2e/conf.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/front/e2e/conf.js b/front/e2e/conf.js index 208428e..56f01db 100644 --- a/front/e2e/conf.js +++ b/front/e2e/conf.js @@ -3,8 +3,9 @@ const config = { seleniumAddress: 'http://localhost:4444/wd/hub', specs: ['spec.js'] }; - +console.log('travis process env : ', process.env.TRAVIS); if (process.env.TRAVIS) { + console.log("TRAVISSSSSSSS !!!!!!!") config.sauceUser = process.env.SAUCE_USERNAME; config.sauceKey = process.env.SAUCE_ACCESS_KEY; config.capabilities = { @@ -12,6 +13,8 @@ if (process.env.TRAVIS) { 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 'build': process.env.TRAVIS_BUILD_NUMBER }; + config.chromeOnly = true; + config.directConnect = true; } // conf.js From 0bb7dcabadc5335d950811da733f5ffd2f3df3e0 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 14:28:31 +0100 Subject: [PATCH 072/132] travis: simple update webdriver before launch e2e :D abaweee --- .travis.yml | 1 + front/e2e/conf.js | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b3c559..d04173c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ script: nvm install node; npm install; xvfb-run npm test; + npm run webdriver:update; xvfb-run npm run e2e; cd ..; fi' diff --git a/front/e2e/conf.js b/front/e2e/conf.js index 56f01db..aaa3347 100644 --- a/front/e2e/conf.js +++ b/front/e2e/conf.js @@ -3,9 +3,7 @@ const config = { seleniumAddress: 'http://localhost:4444/wd/hub', specs: ['spec.js'] }; -console.log('travis process env : ', process.env.TRAVIS); if (process.env.TRAVIS) { - console.log("TRAVISSSSSSSS !!!!!!!") config.sauceUser = process.env.SAUCE_USERNAME; config.sauceKey = process.env.SAUCE_ACCESS_KEY; config.capabilities = { From 6525348f844bb9c2038b749db5d9428fc239c643 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 15:06:43 +0100 Subject: [PATCH 073/132] travis: split script and before script to have error status code on dedicated test phase --- .travis.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index d04173c..7b66e2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,17 +24,16 @@ env: before_install: - cp config/settings.xml ~/.m2/settings.xml - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc +- - 'if [ "$MODULE" = "core/broadcaster" ]; then + cd front; + npm install; + npm run webdriver:update; + cd ..; + fi'; script: - mvn -f $MODULE/pom.xml clean deploy -- 'if [ "$MODULE" = "core/broadcaster" ]; then - cd front; - nvm install node; - npm install; - xvfb-run npm test; - npm run webdriver:update; - xvfb-run npm run e2e; - cd ..; - fi' +- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm test; fi'; +- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm run e2e; fi'; deploy: - provider: cloudfoundry api: https://api.run.pivotal.io From 69c0c5c553d720d08e84a345c0ef0349c4279ab6 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 15:07:49 +0100 Subject: [PATCH 074/132] travis: fix syntax error --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7b66e2e..d7bd11b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ env: before_install: - cp config/settings.xml ~/.m2/settings.xml - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc -- - 'if [ "$MODULE" = "core/broadcaster" ]; then +- 'if [ "$MODULE" = "core/broadcaster" ]; then cd front; npm install; npm run webdriver:update; From 54441bb1b29c1d9b315a96c4937078c1022ce7c3 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 15:16:46 +0100 Subject: [PATCH 075/132] travis: wtf travis ? yml is correct :/ no ? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d7bd11b..87cf6bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ before_install: - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc - 'if [ "$MODULE" = "core/broadcaster" ]; then cd front; + nvm install node npm install; npm run webdriver:update; cd ..; From d5669e90b8a3e777c0eea5d7029e171d1d2dbe38 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 15:39:25 +0100 Subject: [PATCH 076/132] travis: ; in yml :@ --- .travis.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 87cf6bf..65fbe5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,6 @@ sudo: required addons: firefox: latest sauce_connect: true - apt: - sources: - - google-chrome - packages: - - google-chrome-stable env: matrix: - MODULE=core @@ -26,15 +21,15 @@ before_install: - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc - 'if [ "$MODULE" = "core/broadcaster" ]; then cd front; - nvm install node + nvm install node; npm install; npm run webdriver:update; cd ..; - fi'; + fi' script: - mvn -f $MODULE/pom.xml clean deploy -- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm test; fi'; -- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm run e2e; fi'; +- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm test; fi' +- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm run e2e; fi' deploy: - provider: cloudfoundry api: https://api.run.pivotal.io From 760581a7a3f912c351a494679e0abfdc4e84c66d Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 16:02:48 +0100 Subject: [PATCH 077/132] travis: need to redirect to front before launching test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 65fbe5f..dfbfe75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_install: fi' script: - mvn -f $MODULE/pom.xml clean deploy -- 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm test; fi' +- 'if [ "$MODULE" = "core/broadcaster" ]; then cd front; xvfb-run npm test; fi' - 'if [ "$MODULE" = "core/broadcaster" ]; then xvfb-run npm run e2e; fi' deploy: - provider: cloudfoundry From 229a460d9d8687c988a85ea15b0ef4d5c2955771 Mon Sep 17 00:00:00 2001 From: nathandm Date: Fri, 13 Jan 2017 17:04:55 +0100 Subject: [PATCH 078/132] konami: new konami code to allow style customisation this Konami display a new panel that allow style customisation This could be usefull for futur design phase and force ShadyCss definition and used :) --- front/src/components/app-artifact-list.html | 11 +- front/src/components/app-orga.html | 139 ++++-------- front/src/components/app-view.html | 4 +- front/src/components/color-konami.html | 213 ++++++++++++++++++ front/src/reactivity-shell.html | 51 +++-- front/src/shell/navbar/navbar.html | 8 +- front/src/shell/pages/404/view-404.html | 6 +- .../pages/organisation/organisation.html | 2 +- .../pages/organisations/organisations.html | 4 +- front/src/shell/pages/settings/settings.html | 60 +++-- front/src/shell/pages/settings/settings.js | 9 - 11 files changed, 331 insertions(+), 176 deletions(-) create mode 100644 front/src/components/color-konami.html diff --git a/front/src/components/app-artifact-list.html b/front/src/components/app-artifact-list.html index f650d23..b7178ee 100644 --- a/front/src/components/app-artifact-list.html +++ b/front/src/components/app-artifact-list.html @@ -1,5 +1,4 @@ - From 475721ede20d4527f8151d4d3e5d6cda1a8442c6 Mon Sep 17 00:00:00 2001 From: qlevaslot Date: Tue, 21 Feb 2017 23:15:14 +0100 Subject: [PATCH 102/132] front-build: update to latest version of polymer-build --- front/gulp_tasks/build.task.js | 115 ++++++++++++++++++---- front/gulp_tasks/clean.task.js | 8 -- front/gulp_tasks/compile.task.js | 9 -- front/gulp_tasks/polymer-build.task.js | 127 ------------------------- front/gulpfile.js | 6 +- front/package.json | 2 +- front/sw-precache-config.js | 25 +++++ 7 files changed, 125 insertions(+), 167 deletions(-) delete mode 100644 front/gulp_tasks/clean.task.js delete mode 100644 front/gulp_tasks/compile.task.js delete mode 100644 front/gulp_tasks/polymer-build.task.js create mode 100644 front/sw-precache-config.js diff --git a/front/gulp_tasks/build.task.js b/front/gulp_tasks/build.task.js index e5d09e1..7200de6 100644 --- a/front/gulp_tasks/build.task.js +++ b/front/gulp_tasks/build.task.js @@ -1,26 +1,107 @@ +'use strict'; + +const del = require('del'); const gulp = require('gulp'); -const project = require('./polymer-build.task.js'); -// const compile = require('./compile.task.js'); - -// The source task will split all of your source files into one big ReadableStream. Source files are those in src/** as -// well as anything added to the sourceGlobs property of polymer.json. -function source() { - return project.splitSource() - // Add your own build tasks here! - //.pipe(gulpif('**/*.{png,gif,jpg,svg}', images.minify())) - //.pipe(gulpif(/\.js$/, compile())) - .pipe(project.rejoin()); // Call rejoin when you're finished +const mergeStream = require('merge-stream'); +const polymerBuild = require('polymer-build'); + +const swPrecacheConfig = require('../sw-precache-config.js'); +const polymerJson = require('../polymer.json'); +const polymerProject = new polymerBuild.PolymerProject(polymerJson); +const buildDirectory = 'dist'; + +/** + * Waits for the given ReadableStream + */ +function waitFor(stream) { + return new Promise((resolve, reject) => { + stream.on('end', resolve); + stream.on('error', reject); + }); } -// The dependencies task will split all of your bower_components files into one big ReadableStream -function dependencies() { - return project.splitDependencies() - .pipe(project.rejoin()); +function build() { + return new Promise((resolve, reject) => { + + // Lets create some inline code splitters in case you need them later in your build. + let sourcesStreamSplitter = new polymerBuild.HtmlSplitter(); + let dependenciesStreamSplitter = new polymerBuild.HtmlSplitter(); + + // Okay, so first thing we do is clear the build directory + console.log(`Deleting ${buildDirectory} directory...`); + del([buildDirectory]) + .then(() => { + + // Let's start by getting your source files. These are all the files + // in your `src/` directory, or those that match your polymer.json + // "sources" property if you provided one. + let sourcesStream = polymerProject.sources() + + // If you want to optimize, minify, compile, or otherwise process + // any of your source code for production, you can do so here before + // merging your sources and dependencies together. + // .pipe(gulpif(/\.(png|gif|jpg|svg)$/, imagemin())) + + // The `sourcesStreamSplitter` created above can be added here to + // pull any inline styles and scripts out of their HTML files and + // into seperate CSS and JS files in the build stream. Just be sure + // to rejoin those files with the `.rejoin()` method when you're done. + .pipe(sourcesStreamSplitter.split()) + + // Uncomment these lines to add a few more example optimizations to your source files. + // .pipe(gulpif(/\.js$/, uglify())) // Install gulp-uglify to use + // .pipe(gulpif(/\.css$/, cssSlam())) // Install css-slam to use + // .pipe(gulpif(/\.html$/, htmlMinifier())) // Install gulp-html-minify to use + + // Remember, you need to rejoin any split inline code when you're done. + .pipe(sourcesStreamSplitter.rejoin()); + + + // Similarly, you can get your dependencies seperately and perform + // any dependency-only optimizations here as well. + let dependenciesStream = polymerProject.dependencies() + .pipe(dependenciesStreamSplitter.split()) + // Add any dependency optimizations here. + .pipe(dependenciesStreamSplitter.rejoin()); + + + // Okay, now let's merge your sources & dependencies together into a single build stream. + let buildStream = mergeStream(sourcesStream, dependenciesStream) + .once('data', () => { + console.log('Analyzing build dependencies...'); + }); + + // If you want bundling, pass the stream to polymerProject.bundler. + // This will bundle dependencies into your fragments so you can lazy + // load them. + buildStream = buildStream.pipe(polymerProject.bundler); + + // Okay, time to pipe to the build directory + buildStream = buildStream.pipe(gulp.dest(buildDirectory)); + + // waitFor the buildStream to complete + return waitFor(buildStream); + }) + .then(() => { + // Okay, now let's generate the Service Worker + console.log('Generating the Service Worker...'); + return polymerBuild.addServiceWorker({ + project: polymerProject, + buildRoot: buildDirectory, + bundled: true, + swPrecacheConfig: swPrecacheConfig + }); + }) + .then(() => { + // You did it! + console.log('Build complete!'); + resolve(); + }); + }); } module.exports = { - source, - dependencies, + build, }; diff --git a/front/gulp_tasks/clean.task.js b/front/gulp_tasks/clean.task.js deleted file mode 100644 index 80addd9..0000000 --- a/front/gulp_tasks/clean.task.js +++ /dev/null @@ -1,8 +0,0 @@ -const del = require('del'); - -// Clean the build directory -function clean() { - return del([global.config.build.rootDirectory], {dot: true}); -} - -module.exports = clean; diff --git a/front/gulp_tasks/compile.task.js b/front/gulp_tasks/compile.task.js deleted file mode 100644 index dac30f9..0000000 --- a/front/gulp_tasks/compile.task.js +++ /dev/null @@ -1,9 +0,0 @@ -const babel = require('gulp-babel'); - -// Compile javascript -// Mainly used to transform the javascript part of the polymer components -function compile() { - return babel({ "presets": ["es2016"] }); -} - -module.exports = compile; diff --git a/front/gulp_tasks/polymer-build.task.js b/front/gulp_tasks/polymer-build.task.js deleted file mode 100644 index 168350c..0000000 --- a/front/gulp_tasks/polymer-build.task.js +++ /dev/null @@ -1,127 +0,0 @@ -const path = require('path'); -const gulp = require('gulp'); -const mergeStream = require('merge-stream'); -const polymer = require('polymer-build'); - -const polymerJSON = require('../polymer.json'); -const project = new polymer.PolymerProject(polymerJSON); -const bundledPath = `${global.config.build.rootDirectory}/${global.config.build.bundledDirectory}`; -const unbundledPath = `${global.config.build.rootDirectory}/${global.config.build.unbundledDirectory}`; - -// This is the heart of polymer-build, and exposes much of the -// work that Polymer CLI usually does for you -// There are tasks to split the source files and dependency files into -// streams, and tasks to rejoin them and output service workers -// You should not need to modify anything in this file -// If you find that you can't accomplish something because of the way -// this module is structured please file an issue -// https://github.com/PolymerElements/generator-polymer-init-custom-build/issues - -// Returns a ReadableStream of all the source files -// Source files are those in src/** as well as anything -// added to the sourceGlobs property of polymer.json -function splitSource() { - return project.sources().pipe(project.splitHtml()); -} - -// Returns a ReadableStream of all the dependency files -// Dependency files are those in bower_components/** -function splitDependencies() { - return project.dependencies().pipe(project.splitHtml()); -} - -// Returns a WriteableStream to rejoin all split files -function rejoin() { - return project.rejoinHtml(); -} - -// Returns a function which accepts refernces to functions that generate -// ReadableStreams. These ReadableStreams will then be merged, and used to -// generate the bundled and unbundled versions of the site. -// Takes an argument for the user to specify the kind of output they want -// either bundled or unbundled. If this argument is omitted it will output both -function merge(source, dependencies) { - return function output() { - const mergedFiles = mergeStream(source(), dependencies()); - // .pipe(project.analyzer); //TODO investigate why I have to comment that - const bundleType = global.config.build.bundleType; - let outputs = []; - - if (bundleType === 'both' || bundleType === 'bundled') { - outputs.push(writeBundledOutput(polymer.forkStream(mergedFiles))); - } - if (bundleType === 'both' || bundleType === 'unbundled') { - outputs.push(writeUnbundledOutput(polymer.forkStream(mergedFiles))); - } - - return Promise.all(outputs); - }; -} - -// Run the files through a bundling step which will vulcanize/shard them -// then output to the dest dir -function writeBundledOutput(stream) { - return new Promise(resolve => { - stream.pipe(project.bundler) - .pipe(gulp.dest(bundledPath)) - .on('end', resolve); - }); -} - -// Just output files to the dest dir without bundling. This is for projects that -// use HTTP/2 server push -function writeUnbundledOutput(stream) { - return new Promise(resolve => { - stream.pipe(gulp.dest(unbundledPath)) - .on('end', resolve); - }); -} - -// Returns a function which takes an argument for the user to specify the kind -// of bundle they're outputting (either bundled or unbundled) and generates a -// service worker for that bundle. -// If this argument is omitted it will create service workers for both bundled -// and unbundled output -function serviceWorker() { - const bundleType = global.config.build.bundleType; - let workers = []; - - if (bundleType === 'both' || bundleType === 'bundled') { - workers.push(writeBundledServiceWorker()); - } - if (bundleType === 'both' || bundleType === 'unbundled') { - workers.push(writeUnbundledServiceWorker()); - } - return Promise.all(workers); -} - -// Returns a Promise to generate a service worker for bundled output -function writeBundledServiceWorker() { - global.config.swPrecacheConfig.stripPrefix = bundledPath; - return polymer.addServiceWorker({ - project: project, - buildRoot: bundledPath, - swPrecacheConfig: global.config.swPrecacheConfig, - path: global.config.serviceWorkerPath, - bundled: true - }); -} - -// Returns a Promise to generate a service worker for unbundled output -function writeUnbundledServiceWorker() { - global.config.swPrecacheConfig.stripPrefix = unbundledPath; - return polymer.addServiceWorker({ - project: project, - buildRoot: unbundledPath, - swPrecacheConfig: global.config.swPrecacheConfig, - path: global.config.serviceWorkerPath - }); -} - -module.exports = { - splitSource: splitSource, - splitDependencies: splitDependencies, - rejoin: rejoin, - merge: merge, - serviceWorker: serviceWorker -}; diff --git a/front/gulpfile.js b/front/gulpfile.js index a6fcc2e..41bd1ef 100644 --- a/front/gulpfile.js +++ b/front/gulpfile.js @@ -48,8 +48,6 @@ global.config = { }; const buildTask = require('./gulp_tasks/build.task.js'); -const project = require('./gulp_tasks/polymer-build.task.js'); -const clean = require('./gulp_tasks/clean.task.js'); const options = { target: 'http://localhost:8080', @@ -159,7 +157,5 @@ gulp.task('dist-domain-api', (done) => { gulp.task('dist', gulp.series("dist-clean", "dist-bower", "dist-src", "dist-index", "staticfile", "dist-domain-api")); gulp.task('dist2', gulp.series([ - clean, - project.merge(buildTask.source, buildTask.dependencies), - project.serviceWorker, + buildTask.build, ])); \ No newline at end of file diff --git a/front/package.json b/front/package.json index 61be779..3df0bbc 100644 --- a/front/package.json +++ b/front/package.json @@ -46,7 +46,7 @@ "http-proxy-middleware": "^0.17.3", "js-yaml": "^3.7.0", "merge-stream": "^1.0.1", - "polymer-build": "^0.6.0", + "polymer-build": "^0.8.1", "protractor": "^5.0.0", "web-component-tester": "^5.0.0", "webdriver-manager": "^11.1.1" diff --git a/front/sw-precache-config.js b/front/sw-precache-config.js new file mode 100644 index 0000000..5d93c2a --- /dev/null +++ b/front/sw-precache-config.js @@ -0,0 +1,25 @@ +module .exports = { + staticFileGlobs: [ + '/index.html', + '/manifest.json', + '/bower_components/webcomponentsjs/webcomponents-lite.min.js', + '/src/**/*.*', + ], + navigateFallback: '/index.html', + importScripts: ['/bower_components/sw-toolbox/sw-toolbox.js'], + runtimeCaching: [], + // See sample below for actual runtime caching: + //runtimeCaching: [{ + // urlPattern: /\/network-first\//, + // handler: 'networkFirst' + //}, { + // urlPattern: /\/cache-first\//, + // handler: 'cacheFirst', + // options: { + // cache: { + // maxEntries: 5, + // name: 'cache-first' + // } + // } + //}], +}; \ No newline at end of file From e8a0feb79e5cc1f03c293757f16e3fa74ab930ac Mon Sep 17 00:00:00 2001 From: gdrouet Date: Wed, 22 Feb 2017 19:41:22 +0100 Subject: [PATCH 103/132] dist: switch to bundle task --- front/gulpfile.js | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/front/gulpfile.js b/front/gulpfile.js index 41bd1ef..201eaef 100644 --- a/front/gulpfile.js +++ b/front/gulpfile.js @@ -57,22 +57,6 @@ const options = { } }; -const deleteFolderRecursive = (path) => { - if (fs.existsSync(path) ) { - fs.readdirSync(path).forEach((file,index) => { - var curPath = path + "/" + file; - - if( fs.lstatSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath); - } else { // delete file - fs.unlinkSync(curPath); - } - }); - - fs.rmdirSync(path); - } -}; - // Watch scss AND html files, doing different things with each. gulp.task('default', () => { // Serve files from the root of this project @@ -94,27 +78,6 @@ gulp.task('default', () => { }); }); -gulp.task('dist-clean', (done) => { - deleteFolderRecursive('./dist'); - done(); -}); - -gulp.task('dist-bower', (done) => { - gulp.src(['./bower_components/**/*.{js,html}']) - .pipe(gulp.dest("./dist/bower_components")); - done(); -}); - -gulp.task('dist-src', (done) => { - gulp.src(['./src/**/*']).pipe(gulp.dest("./dist/src")); - done(); -}); - -gulp.task('dist-index', (done) => { - gulp.src(['./index.html']).pipe(gulp.dest("./dist")); - done(); -}); - gulp.task('staticfile', (done) => { file('./dist/Staticfile', "", { src: true }).pipe(gulp.dest('./')); done(); @@ -154,8 +117,6 @@ gulp.task('dist-domain-api', (done) => { done(); }); -gulp.task('dist', gulp.series("dist-clean", "dist-bower", "dist-src", "dist-index", "staticfile", "dist-domain-api")); - -gulp.task('dist2', gulp.series([ - buildTask.build, +gulp.task('dist', gulp.series([ + buildTask.build, "staticfile", "dist-domain-api", ])); \ No newline at end of file From b00e3e908901e33d2af716f67a1ecece2e88f190 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Thu, 23 Feb 2017 23:23:31 +0100 Subject: [PATCH 104/132] travis: externalize all scripts --- .travis.yml | 22 ++++------------------ script/dist.sh | 5 +++++ script/front-test.sh | 8 ++++++++ script/node-npm.sh | 6 ++++++ script/run-app.sh | 2 ++ script/stop-app.sh | 5 ++++- 6 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 script/dist.sh create mode 100644 script/front-test.sh create mode 100644 script/node-npm.sh diff --git a/.travis.yml b/.travis.yml index 74b97c5..0bf6aba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,28 +22,14 @@ before_install: - chmod +x script/*.sh - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/install-couchbase.sh; fi' install: -- 'if [ "$MODULE" = "core/broadcaster" ]; then - cd front; - nvm install node; - npm install; - cd ..; - fi' + - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/node-npm.sh; fi' script: - mvn -f $MODULE/pom.xml clean deploy - - 'if [ "$MODULE" = "core/broadcaster" ]; then - ./script/run-app.sh; - cd front; - npm run webdriver:update; - xvfb-run npm test; - xvfb-run npm run e2e; - cd ..; - fi' + - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/front-test.sh; fi' after_script: - - 'if [ "$MODULE" = "core/broadcaster" ]; then sudo service couchbase-server stop; fi' + - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/stop-app.sh; fi' after_success: -- 'cd front; - npm run dist; - cd ..' + - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/dist.sh; fi' deploy: - provider: cloudfoundry api: https://api.run.pivotal.io diff --git a/script/dist.sh b/script/dist.sh new file mode 100644 index 0000000..40f62fd --- /dev/null +++ b/script/dist.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd front +npm run dist +cd .. \ No newline at end of file diff --git a/script/front-test.sh b/script/front-test.sh new file mode 100644 index 0000000..27b14c2 --- /dev/null +++ b/script/front-test.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +./script/run-app.sh +cd front +npm run webdriver:update +xvfb-run npm test +xvfb-run npm run e2e +cd .. \ No newline at end of file diff --git a/script/node-npm.sh b/script/node-npm.sh new file mode 100644 index 0000000..1a36a57 --- /dev/null +++ b/script/node-npm.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd front +nvm install node +npm install +cd .. \ No newline at end of file diff --git a/script/run-app.sh b/script/run-app.sh index bb9ef67..2214d63 100644 --- a/script/run-app.sh +++ b/script/run-app.sh @@ -1,3 +1,5 @@ +#!/bin/bash + java -jar core/broadcaster/target/broadcaster-0.1.0-SNAPSHOT.jar& echo $! > script/broadcaster.pid cd front diff --git a/script/stop-app.sh b/script/stop-app.sh index 9fcec71..2c3ed31 100644 --- a/script/stop-app.sh +++ b/script/stop-app.sh @@ -1,2 +1,5 @@ +#!/bin/bash + kill -9 `cat script/front.pid` -kill -9 `cat script/broadcaster.pid` \ No newline at end of file +kill -9 `cat script/broadcaster.pid` +sudo service couchbase-server stop \ No newline at end of file From 45af74c3a61f1e7e4c3af1f9574228242c86de79 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 25 Feb 2017 10:50:14 +0100 Subject: [PATCH 105/132] bower: fix sw-toolbox version After 3.4, the sw-toolbox.js file is not installed anymore while the gulp task refers it. --- front/bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/bower.json b/front/bower.json index 86a8afa..bbb3c41 100644 --- a/front/bower.json +++ b/front/bower.json @@ -19,7 +19,7 @@ }, "devDependencies": { "web-component-tester": "^4.0.0", - "sw-toolbox": "^3.4.0" + "sw-toolbox": "3.4.0" }, "resolutions": { "lodash": "^3.7.0", From 7b8d93aed5a5354caddd7a22b5aa02a92d509233 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 25 Feb 2017 11:13:47 +0100 Subject: [PATCH 106/132] npm: explicitly declare the through2 dependency --- front/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/package.json b/front/package.json index 3df0bbc..1c2cbfb 100644 --- a/front/package.json +++ b/front/package.json @@ -49,6 +49,7 @@ "polymer-build": "^0.8.1", "protractor": "^5.0.0", "web-component-tester": "^5.0.0", - "webdriver-manager": "^11.1.1" + "webdriver-manager": "^11.1.1", + "through2": "^2.0.3" } } From b2a59533cff2c11573dab987b936ec645c421f48 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 25 Feb 2017 11:22:42 +0100 Subject: [PATCH 107/132] maven: migrate spring dependencies to the new webflux module --- core/broadcaster/pom.xml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 2108948..6936e2a 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -37,13 +37,6 @@ pom import - - org.springframework.boot.experimental - spring-boot-dependencies-web-reactive - ${spring-web-reactive.version} - pom - import - @@ -180,8 +173,8 @@ reactor-netty - org.springframework.boot.experimental - spring-boot-starter-web-reactive + org.springframework.boot + spring-boot-starter-webflux org.springframework.boot @@ -195,8 +188,8 @@ tomcat - org.springframework.boot.experimental - spring-boot-starter-web-reactive + org.springframework.boot + spring-boot-starter-webflux From 0a5c659c2f2b5eab22d7618cd663ae422c5cd8fd Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 25 Feb 2017 15:15:24 +0100 Subject: [PATCH 108/132] travis: install nvm manually and fix e2e test --- .travis.yml | 9 ++------- front/package.json | 2 +- script/dist.sh | 5 ----- script/front-end.sh | 29 +++++++++++++++++++++++++++++ script/front-test.sh | 8 -------- script/node-npm.sh | 6 ------ script/run-app.sh | 9 --------- script/stop-app.sh | 1 + 8 files changed, 33 insertions(+), 36 deletions(-) delete mode 100644 script/dist.sh create mode 100644 script/front-end.sh delete mode 100644 script/front-test.sh delete mode 100644 script/node-npm.sh delete mode 100644 script/run-app.sh diff --git a/.travis.yml b/.travis.yml index 0bf6aba..07cf7b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ sudo: required language: java jdk: - oraclejdk8 -node_js: -- "6" addons: firefox: latest sauce_connect: true @@ -14,6 +12,7 @@ env: - MODULE=core/java-lib - MODULE=core/broadcaster global: + - NODEJS_VERSION: 6.10.0 - secure: jyieF62Bdxn8KzePW73qAWOsV6n83gIGDpKGpgbpbCDG+R5AcMqFSXf32A7So148YczZIjlhWUx85Vrol9NURpimxcFfZU9nKCPjZt7QpGuzXInbxy8Qrze1I/PN40nMVGxNEam7pwXIMZwbOFPv/PG+fDw7BCDSfsoTwTMbk1FvjhpBngo6DBtbyld30HEhIVSSLhROgcQ7zzcfHYdu9IxvnDFnuNict1m1+HGJf0jDAbFRzGteMq7/tu+rM9HHd5UyCSI2sGWo4P3tAcXw/l3VV/MxhG6cRofvQQEY7H+2JpJ5U6CDprrN7sOUrc777cmOzXzB1sYLaNNniQiScQtZJTYcJbhPF/rOYn8FjrFDMxC0EB7oQQ58Z4dUViKNAdR54zxFwm/OqDssAV43fAOFfkiZGfdKjR+CQQMWRD7UdaCfsy3w/xFw5KiNObyUtvbBJABVYWjsIIF0yU20Qu8Cr5DCfUjUTyNiWz/LjN3cQjtv6HB7A3UWc/ohjhU/GZoWvvJ5WySKLMuL8msH5/R3UE+u8TZYTi9pSG/k6BSoVO4DRAmvG/7FdepwHJYnW1Ol1Bd2u0OdqiwhByu0yDH9jolJ/VYBq8A880nU+dW5SVhwTNSvLgo3jclr4IwkCydZvZ6YjiqpKSqW69TRWyPLOFVLtn943yFX89OXhwY= - secure: Y0Ck+5ZLdbv2qC5uySGZZmuU1++fBvLOtUquAiFn4IPXzaMTbURqr0jY6+R/ibiJdnTgKk4H65Gl4S2d2UPSVTeIUoxnzSSiUHhoxp1FUrGuK8wxuk0ZMffRIMWODRxLJsuFKXTt7NpwGmDx53Kzzp0cbMo9ilkVFJu+6PBxmTC2lRZwIeHFo4U56DjNwWaHA3dXwfiv1S6tsfem3kxGHb9SsfN4UGyXTRqKZBK+DwEs07Uwwq6FkCqsSzcV42uTx2t9qRrVbs3elIPdNKIitEE/V+juQTg94c3BHspXHZhgExByAm/2SSOZaE2syyrFGPSPRf1XKbzwkiGCJiCWzT8naOZB8CC6V4QxDwbrh6nU9lH2BQdcDZj9/pcT6HPc8nhoBKwl4Rzpnsva4b70cZE57KaRwiDQ4lUvFPJ3YepPiz0QKNnEPYHAbLFIMBvsVcMGGkKg4aMSiBfCXu62PTAAqUNQJKRVEIpYAs6AHE3IUUsh+gZ24yWddEHRE3mUvi3vIYnNUjlt0e9fTqfitl1wMV68CZb5llm19jLYFZ0uX54d0xkCtdmNWBvZBip4lVpuR/qQYgLp2LOiEqgzFNLg4x6DxRabUAZ0tP/daIQ2WBBTXKIkxCk2Am/xwFCyz9Bzb97DTSUjvdtI++bGd1nn6LN8AZ01RhO4I5Rff/0= before_install: @@ -21,15 +20,11 @@ before_install: - echo "MAVEN_OPTS='-Xmx1024m'" > ~/.mavenrc - chmod +x script/*.sh - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/install-couchbase.sh; fi' -install: - - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/node-npm.sh; fi' script: - mvn -f $MODULE/pom.xml clean deploy - - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/front-test.sh; fi' + - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/front-end.sh; fi' after_script: - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/stop-app.sh; fi' -after_success: - - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/dist.sh; fi' deploy: - provider: cloudfoundry api: https://api.run.pivotal.io diff --git a/front/package.json b/front/package.json index 1c2cbfb..956c71a 100644 --- a/front/package.json +++ b/front/package.json @@ -49,7 +49,7 @@ "polymer-build": "^0.8.1", "protractor": "^5.0.0", "web-component-tester": "^5.0.0", - "webdriver-manager": "^11.1.1", + "webdriver-manager": "^12.0.2", "through2": "^2.0.3" } } diff --git a/script/dist.sh b/script/dist.sh deleted file mode 100644 index 40f62fd..0000000 --- a/script/dist.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -cd front -npm run dist -cd .. \ No newline at end of file diff --git a/script/front-end.sh b/script/front-end.sh new file mode 100644 index 0000000..f54393d --- /dev/null +++ b/script/front-end.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +rm -rf ~/.nvm +git clone https://github.com/creationix/nvm.git ~/.nvm +cd ~/.nvm +git checkout `git describe --abbrev=0 --tags` +source ~/.nvm/nvm.sh +nvm install $NODEJS_VERSION +cd $TRAVIS_BUILD_DIR/front +npm install + +cd $TRAVIS_BUILD_DIR +java -jar core/broadcaster/target/broadcaster-0.1.0-SNAPSHOT.jar& +echo $! > script/broadcaster.pid +cd $TRAVIS_BUILD_DIR/front +npm start& +echo $! > ../script/front.pid +sleep 10 + +cd $TRAVIS_BUILD_DIR/front +npm run webdriver:update +npm run webdriver:start& +echo $! > ../script/webdriver.pid +sleep 5 +xvfb-run npm test +xvfb-run npm run e2e + +npm run dist +cd $TRAVIS_BUILD_DIR \ No newline at end of file diff --git a/script/front-test.sh b/script/front-test.sh deleted file mode 100644 index 27b14c2..0000000 --- a/script/front-test.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -./script/run-app.sh -cd front -npm run webdriver:update -xvfb-run npm test -xvfb-run npm run e2e -cd .. \ No newline at end of file diff --git a/script/node-npm.sh b/script/node-npm.sh deleted file mode 100644 index 1a36a57..0000000 --- a/script/node-npm.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -cd front -nvm install node -npm install -cd .. \ No newline at end of file diff --git a/script/run-app.sh b/script/run-app.sh deleted file mode 100644 index 2214d63..0000000 --- a/script/run-app.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -java -jar core/broadcaster/target/broadcaster-0.1.0-SNAPSHOT.jar& -echo $! > script/broadcaster.pid -cd front -npm start& -echo $! > ../script/front.pid -cd .. -sleep 10 \ No newline at end of file diff --git a/script/stop-app.sh b/script/stop-app.sh index 2c3ed31..0528dfb 100644 --- a/script/stop-app.sh +++ b/script/stop-app.sh @@ -2,4 +2,5 @@ kill -9 `cat script/front.pid` kill -9 `cat script/broadcaster.pid` +kill -9 `cat script/webdriver.pid` sudo service couchbase-server stop \ No newline at end of file From ca0cafb498f58b4056990fbd5a46b9c34097fe15 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sun, 26 Feb 2017 18:12:56 +0100 Subject: [PATCH 109/132] maven: build a source artifact --- core/pom.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index a6c0fdd..c37a5f7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -16,6 +16,8 @@ github 0.12 + + 3.0.1 @@ -38,6 +40,24 @@ + + org.apache.maven.plugins + maven-source-plugin + ${source-plugin.version} + + true + + + + attach-sources + + jar + aggregate + + + + + com.github.github From 55179785d3709ca81f0a2a41173e4395f003f132 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sun, 26 Feb 2017 18:19:06 +0100 Subject: [PATCH 110/132] javadoc: add missing comment --- .../src/main/java/io/reactivity/core/lib/event/Event.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java index c25b457..9f3e2d6 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/event/Event.java @@ -1,5 +1,5 @@ /* - * The MIT License (MIT) Copyright (c) 2016 The reactivity authors + * The MIT License (MIT) Copyright (c) 2017 The reactivity authors * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the @@ -28,7 +28,7 @@ * * @author Guillaume DROUET * @since 0.1.0 - * @param + * @param the type of {@link ReactivityEntity} */ public class Event { From 488585a358e34f495813708480fc4eb8aae9f45c Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sun, 26 Feb 2017 18:20:50 +0100 Subject: [PATCH 111/132] maven: build a javadoc artifact --- core/pom.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index c37a5f7..9d02c52 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -18,6 +18,7 @@ 0.12 3.0.1 + 2.10.4 @@ -58,6 +59,26 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + ${javadoc-plugin.version} + + UTF-8 + + http://docs.oracle.com/javase/8/docs/api/ + http://docs.spring.io/spring/docs/current/javadoc-api/ + + + + + + jar + + + + + com.github.github From bcb6c0f075ca29878c9ed8c3e8d440894b7c2b64 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sun, 5 Mar 2017 13:28:33 +0100 Subject: [PATCH 112/132] test: use spring boot test support as it now works with spring 5 --- .../core/broadcaster/RepositoryTest.java | 6 +- .../core/broadcaster/TestConfig.java | 56 ------------------- 2 files changed, 3 insertions(+), 59 deletions(-) delete mode 100644 core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java index 3422799..c6d34c9 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -31,7 +31,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import reactor.core.publisher.Flux; @@ -53,7 +53,7 @@ * @since 0.1.0 */ @RunWith(SpringRunner.class) -@ContextConfiguration(classes = TestConfig.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(properties = "build.version=0.1.0") public class RepositoryTest { @@ -144,7 +144,7 @@ public void testVersions() throws InterruptedException { .put("categories", JsonObject.from(createCategories(id))))); // Wait a little bit and let couchbase store the document - Thread.sleep(300L); + Thread.sleep(400L); final List list = Flux.from(couchbaseReactivityRepository.findArtifactFromView( new ArtifactView( diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java deleted file mode 100644 index 6b35807..0000000 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/TestConfig.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * The MIT License (MIT) Copyright (c) 2016 The reactivity authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - -package io.reactivity.core.broadcaster; - -import io.reactivity.core.broadcaster.config.CouchbaseConfig; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -/** - *

                      - * This configuration class detects environment variable. - * This kind of support is provided by spring boot but the {@code SpringBootApplication} annotation is currently not used. - *

                      - * - * @author Guillaume DROUET - * @since 0.1.0 - */ -@Configuration -@ComponentScan(basePackageClasses = TestConfig.class) -class TestConfig extends CouchbaseConfig { - - /** - *

                      - * Builds a the default instance. - *

                      - */ - public TestConfig() { - final String nodes = System.getenv("REACTIVITY_COUCHBASE_NODES"); - - if (nodes != null) { - setNodes(nodes.split(",")); - } - - final String bucket = System.getenv("REACTIVITY_COUCHBASE_BUCKET"); - - if (bucket != null) { - setBucket(bucket); - } - } -} From 5dd3c591569a0026e2e59246d36c0291a3726a96 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Tue, 7 Mar 2017 11:55:35 +0100 Subject: [PATCH 113/132] restdocs: adds spring-rest-docs This commit introduces the spring-rest-docs support. All EventController endpoinds are documented and generated snippets are pushed to a "doc-snippets" branch that can be used in the reactivity-doc project. Currently unit tests are mixing MockMvc with webflux module, which works even if a more adapted support for webflux tests has to be considered. --- .travis.yml | 4 +- core/broadcaster/pom.xml | 9 +- .../core/broadcaster/web/EventController.java | 15 +- .../reactivity/core/broadcaster/ApiTest.java | 339 ++++++++++++++++++ .../core/broadcaster/RepositoryTest.java | 2 +- .../java/io/reactivity/core/lib/ViewType.java | 1 + reactivity.enc | Bin 0 -> 1680 bytes script/publish-doc-snippets.sh | 51 +++ 8 files changed, 409 insertions(+), 12 deletions(-) create mode 100644 core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java create mode 100644 reactivity.enc create mode 100644 script/publish-doc-snippets.sh diff --git a/.travis.yml b/.travis.yml index 07cf7b3..2e9c021 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ env: - MODULE=core/broadcaster global: - NODEJS_VERSION: 6.10.0 + - ENCRYPTION_LABEL: "094e2d6354aa" + - COMMIT_AUTHOR_EMAIL: "guidrouet@gmail.com" - secure: jyieF62Bdxn8KzePW73qAWOsV6n83gIGDpKGpgbpbCDG+R5AcMqFSXf32A7So148YczZIjlhWUx85Vrol9NURpimxcFfZU9nKCPjZt7QpGuzXInbxy8Qrze1I/PN40nMVGxNEam7pwXIMZwbOFPv/PG+fDw7BCDSfsoTwTMbk1FvjhpBngo6DBtbyld30HEhIVSSLhROgcQ7zzcfHYdu9IxvnDFnuNict1m1+HGJf0jDAbFRzGteMq7/tu+rM9HHd5UyCSI2sGWo4P3tAcXw/l3VV/MxhG6cRofvQQEY7H+2JpJ5U6CDprrN7sOUrc777cmOzXzB1sYLaNNniQiScQtZJTYcJbhPF/rOYn8FjrFDMxC0EB7oQQ58Z4dUViKNAdR54zxFwm/OqDssAV43fAOFfkiZGfdKjR+CQQMWRD7UdaCfsy3w/xFw5KiNObyUtvbBJABVYWjsIIF0yU20Qu8Cr5DCfUjUTyNiWz/LjN3cQjtv6HB7A3UWc/ohjhU/GZoWvvJ5WySKLMuL8msH5/R3UE+u8TZYTi9pSG/k6BSoVO4DRAmvG/7FdepwHJYnW1Ol1Bd2u0OdqiwhByu0yDH9jolJ/VYBq8A880nU+dW5SVhwTNSvLgo3jclr4IwkCydZvZ6YjiqpKSqW69TRWyPLOFVLtn943yFX89OXhwY= - secure: Y0Ck+5ZLdbv2qC5uySGZZmuU1++fBvLOtUquAiFn4IPXzaMTbURqr0jY6+R/ibiJdnTgKk4H65Gl4S2d2UPSVTeIUoxnzSSiUHhoxp1FUrGuK8wxuk0ZMffRIMWODRxLJsuFKXTt7NpwGmDx53Kzzp0cbMo9ilkVFJu+6PBxmTC2lRZwIeHFo4U56DjNwWaHA3dXwfiv1S6tsfem3kxGHb9SsfN4UGyXTRqKZBK+DwEs07Uwwq6FkCqsSzcV42uTx2t9qRrVbs3elIPdNKIitEE/V+juQTg94c3BHspXHZhgExByAm/2SSOZaE2syyrFGPSPRf1XKbzwkiGCJiCWzT8naOZB8CC6V4QxDwbrh6nU9lH2BQdcDZj9/pcT6HPc8nhoBKwl4Rzpnsva4b70cZE57KaRwiDQ4lUvFPJ3YepPiz0QKNnEPYHAbLFIMBvsVcMGGkKg4aMSiBfCXu62PTAAqUNQJKRVEIpYAs6AHE3IUUsh+gZ24yWddEHRE3mUvi3vIYnNUjlt0e9fTqfitl1wMV68CZb5llm19jLYFZ0uX54d0xkCtdmNWBvZBip4lVpuR/qQYgLp2LOiEqgzFNLg4x6DxRabUAZ0tP/daIQ2WBBTXKIkxCk2Am/xwFCyz9Bzb97DTSUjvdtI++bGd1nn6LN8AZ01RhO4I5Rff/0= before_install: @@ -22,7 +24,7 @@ before_install: - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/install-couchbase.sh; fi' script: - mvn -f $MODULE/pom.xml clean deploy - - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/front-end.sh; fi' + - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/publish-doc-snippets.sh; ./script/front-end.sh; fi' after_script: - 'if [ "$MODULE" = "core/broadcaster" ]; then ./script/stop-app.sh; fi' deploy: diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 6936e2a..33383e7 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -25,6 +25,7 @@ 2.0.0.BUILD-SNAPSHOT 0.1.0.BUILD-SNAPSHOT 0.6.0.BUILD-SNAPSHOT + 2.7 @@ -105,13 +106,19 @@ org.springframework.boot spring-boot-starter-test + test + + + + org.springframework.restdocs + spring-restdocs-mockmvc + test org.springframework.security spring-security-core - org.springframework.session spring-session diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java index a0fbdc5..2f75cd2 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java @@ -1,5 +1,5 @@ /* - * The MIT License (MIT) Copyright (c) 2017 The reactivity authors + * The MIT License (MIT) Copyright (c) 2016 The reactivity authors * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the @@ -20,16 +20,13 @@ import io.reactivity.core.broadcaster.service.EventService; import io.reactivity.core.broadcaster.session.ReactivitySessionScope; -import io.reactivity.core.lib.event.Event; import io.reactivity.core.lib.event.Organization; -import io.reactivity.core.lib.ReactivityEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Flux; /** *

                      @@ -59,7 +56,7 @@ public class EventController { *

                      * * @param eventService the event service - * @param authentication the current authentication + * @param authentication the authentication */ @Autowired public EventController(final EventService eventService, final Authentication authentication) { @@ -78,7 +75,7 @@ public EventController(final EventService eventService, final Authentication aut * @return the event flux */ @GetMapping("/load/artifacts/{viewId}/limit/{limit}/maxage/{maxAge}") - Flux> loadArtifactsLteMaxAge( + public Object loadArtifactsLteMaxAge( @PathVariable(name = "viewId") final String viewId, @PathVariable(name = "limit") final int limit, @PathVariable(name = "maxAge") final long maxAge) { @@ -96,7 +93,7 @@ Flux> loadArtifactsLteMaxAge( * @return the event flux */ @GetMapping("/load/artifacts/{viewId}/limit/{limit}/minage/{minAge}") - Flux> loadArtifactsGteMinAge( + public Object loadArtifactsGteMinAge( @PathVariable(name = "viewId") final String viewId, @PathVariable(name = "limit") final int limit, @PathVariable(name = "minAge") final long minAge) { @@ -111,7 +108,7 @@ Flux> loadArtifactsGteMinAge( * @return the organization event flux */ @GetMapping("/load/organizations") - public Flux> loadOrganizations() { + public Object loadOrganizations() { // Retrieve the organizations: member ID can be an arbitrary value as it is currently mocked return eventService.loadOrganizations(authentication.getName()); } @@ -125,7 +122,7 @@ public Flux> loadOrganizations() { * @return the event flux */ @GetMapping("/subscribe/{organizationId}") - Flux> subscribe(@PathVariable(name = "organizationId") final String organizationId) { + public Object subscribe(@PathVariable(name = "organizationId") final String organizationId) { return eventService.subscribe(organizationId); } } diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java new file mode 100644 index 0000000..cff2032 --- /dev/null +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java @@ -0,0 +1,339 @@ +/* + * The MIT License (MIT) Copyright (c) 2017 The reactivity authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +package io.reactivity.core.broadcaster; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; + +import io.reactivity.core.broadcaster.web.EventController; +import io.reactivity.core.lib.ReactivityEntity; +import io.reactivity.core.lib.Version; +import io.reactivity.core.lib.event.Artifact; +import io.reactivity.core.lib.event.ArtifactView; +import io.reactivity.core.lib.event.Error; +import io.reactivity.core.lib.event.Event; +import io.reactivity.core.lib.event.EventType; +import io.reactivity.core.lib.event.Member; +import io.reactivity.core.lib.event.Organization; +import io.reactivity.core.lib.event.Period; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Collections; +import java.util.HashMap; +import java.util.UUID; + +/** + *

                      + * Tests the mocked {@link EventController} and document associated responses. + *

                      + * + * @author Guillaume DROUET + * @since 0.1.0 + */ +@RunWith(SpringRunner.class) +@WebMvcTest(controllers = EventController.class) +@AutoConfigureRestDocs( + outputDir = "target/generated-snippets", + uriHost = "your-reactivity-server", + uriPort = 80 +) +public class ApiTest { + + /** + * Mocked {@link EventController}. + */ + @MockBean + private EventController controller; + + /** + * The mock MVC. + */ + @Autowired + private MockMvc mockMvc; + + /** + * Tests artifact with a max age. + * + * @throws Exception if test fails + */ + @Test + public void loadArtifactsLteMaxAgeTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newArtifactEvent())) + .when(controller).loadArtifactsLteMaxAge( + ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyLong()); + + mockMvc.perform(get("/load/artifacts/myView/limit/1/maxage/1")) + .andExpect(status().isOk()) + .andDo(document("load-artifacts-lte-max-age-example", + responseFields(eventFieldDescriptors(artifactEventFieldDescriptors())))); + } + + /** + * Tests artifact with a max age that fail to load. + * + * @throws Exception if test fails + */ + @Test + public void loadArtifactsLteMaxAgeErrorTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newErrorEvent())) + .when(controller).loadArtifactsLteMaxAge( + ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyLong()); + + mockMvc.perform(get("/load/artifacts/myView/limit/1/maxage/1")) + .andExpect(status().isOk()) + .andDo(document("load-artifacts-lte-max-age-error-example", + responseFields(errorEventFieldDescriptors()))); + } + + /** + * Tests artifact with a min age. + * + * @throws Exception if test fails + */ + @Test + public void loadArtifactsGteMinAgeTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newArtifactEvent())) + .when(controller).loadArtifactsGteMinAge( + ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyLong()); + + mockMvc.perform(get("/load/artifacts/myView/limit/1/minage/1")) + .andExpect(status().isOk()) + .andDo(document("load-artifacts-gte-min-age-example", + responseFields(eventFieldDescriptors(artifactEventFieldDescriptors())))); + } + + /** + * Tests artifact with a min age that fail to load. + * + * @throws Exception if test fails + */ + @Test + public void loadArtifactsGteMinAgeErrorTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newErrorEvent())) + .when(controller).loadArtifactsGteMinAge( + ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyLong()); + + mockMvc.perform(get("/load/artifacts/myView/limit/1/minage/1")) + .andExpect(status().isOk()) + .andDo(document("load-artifacts-gte-min-age-error-example", + responseFields(eventFieldDescriptors(errorEventFieldDescriptors())))); + } + + /** + * Tests load organizations. + * + * @throws Exception if test fails + */ + @Test + public void loadOrganizationsTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newOrganizationEvent())) + .when(controller).loadOrganizations(); + + mockMvc.perform(get("/load/organizations")) + .andExpect(status().isOk()) + .andDo(document("load-organizations-example", + responseFields(eventFieldDescriptors( + fieldWithPath("[].data.name").description("The organization name."), + fieldWithPath("[].data.picture").description("The base64 representation of the organization avatar."), + fieldWithPath("[].data.members").description("The organization's members."), + fieldWithPath("[].data.members[].id").description("The member ID."), + fieldWithPath("[].data.members[].role").description("The member role."))))); + } + + /** + * Tests organization load that fails. + * + * @throws Exception if test fails + */ + @Test + public void loadOrganizationErrorTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newErrorEvent())) + .when(controller).loadOrganizations(); + + mockMvc.perform(get("/load/organizations")) + .andExpect(status().isOk()) + .andDo(document("load-organizations-error-example", + responseFields(errorEventFieldDescriptors()))); + } + + /** + * Tests subscription. + * + * @throws Exception if test fails + */ + @Test + public void subscribeTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newViewEvent())) + .when(controller).subscribe(ArgumentMatchers.anyString()); + + mockMvc.perform(get("/subscribe/1")) + .andExpect(status().isOk()) + .andDo(document("subscribe-example", + responseFields(eventFieldDescriptors( + fieldWithPath("[].data.name").description("The view name."), + fieldWithPath("[].data.organization").description("The organization ID."), + fieldWithPath("[].data.period").description("The period defined for that view."), + fieldWithPath("[].data.period.from").description("From when the period starts."), + fieldWithPath("[].data.period.to").description("When the period ends."), + fieldWithPath("[].data.period.limit").description("Limit the number of results (if 'from' or 'to' is undefined)."), + fieldWithPath("[].data.period.category").description("The optional category containing a date to test with the period (updated by default)."), + fieldWithPath("[].data.type").description("The type of view."))))); + } + + /** + * Tests error subscription. + * + * @throws Exception if test fails + */ + @Test + public void subscribeErrorTest() throws Exception { + Mockito.doAnswer(invocation -> Collections.singletonList(newErrorEvent())) + .when(controller).subscribe(ArgumentMatchers.anyString()); + + mockMvc.perform(get("/subscribe/1")) + .andExpect(status().isOk()) + .andDo(document("subscribe-error-example", + responseFields(errorEventFieldDescriptors()))); + } + + /** + *

                      + * Creates an organization event. + *

                      + * + * @return the organization event + */ + private Event newOrganizationEvent() { + return EventType.READ_ORGANIZATION.newEvent( + new Organization( + new Version("1.0.0"), UUID.randomUUID().toString(), + 1L, + "my-org", + "", + Collections.singletonList(new Member(UUID.randomUUID().toString(), "ADMIN")))); + } + + /** + *

                      + * Creates a view event. + *

                      + * + * @return the view event + */ + private Event newViewEvent() { + return EventType.READ_VIEW.newEvent( + new ArtifactView( + new Version("1.0.0"), UUID.randomUUID().toString(), + 1L, + "my-org", + "my-view", + new Period(0L, 1L, 10, "dueDate"), + "LIST")); + } + + /** + *

                      + * Creates an artifact event. + *

                      + * + * @return the organization event + */ + private Event newArtifactEvent() { + return EventType.READ_ARTIFACT.newEvent( + new Artifact( + new Version("1.0.0"), UUID.randomUUID().toString(), + 1L, + Collections.singletonList("myView"), + new HashMap<>())); + } + + /** + *

                      + * Creates an error event. + *

                      + * + * @return the organization event + */ + private Event newErrorEvent() { + return EventType.ERROR.newEvent(new Error("An error message.")); + } + + /** + *

                      + * Returns an array containing the fields of any error message. + *

                      + * + * @return the field descriptor array + */ + private FieldDescriptor[] errorEventFieldDescriptors() { + return eventFieldDescriptors(fieldWithPath("[].data.message").description("The message")); + } + + /** + *

                      + * Builds an array of {@code DescriptorField} containing the fields always found in an {@link Event} and the + * additional field related to the {@link ReactivityEntity} wrapped by this event. + *

                      + * + * @param additionalFields the data fields + * @return the data field with the wrapping event fields + */ + private FieldDescriptor[] eventFieldDescriptors(final FieldDescriptor ... additionalFields) { + final FieldDescriptor[] eventFields = new FieldDescriptor[] { + fieldWithPath("[].version").description("The application version corresponding to the wrapped entity."), + fieldWithPath("[].snapshot").description("If the application that generated this entity is a snapshot."), + fieldWithPath("[].id").description("The entity ID."), + fieldWithPath("[].event").description("The type of event."), + fieldWithPath("[].updated").description("When the entity was updated."), + }; + + final FieldDescriptor[] retval = new FieldDescriptor[eventFields.length + additionalFields.length]; + System.arraycopy(eventFields, 0, retval, 0, eventFields.length); + System.arraycopy(additionalFields, 0, retval, eventFields.length, additionalFields.length); + + return retval; + } + + /** + *

                      + * Returns the field descriptors of an event wrapping an artifact. + *

                      + * + * @return the field descriptors + */ + private FieldDescriptor[] artifactEventFieldDescriptors() { + return eventFieldDescriptors( + fieldWithPath("[].data.views").description("The ID of views that are able to display the artifact."), + fieldWithPath("[].data.categories").description("The categories (a simple key/value pair) defined in the artifact.")); + } +} diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java index c6d34c9..225fdd6 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -53,7 +53,7 @@ * @since 0.1.0 */ @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest @TestPropertySource(properties = "build.version=0.1.0") public class RepositoryTest { diff --git a/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java b/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java index d7c7cec..b555f32 100644 --- a/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java +++ b/core/java-lib/src/main/java/io/reactivity/core/lib/ViewType.java @@ -24,6 +24,7 @@ *

                      * * @author Guillaume DROUET + * @since 0.1.0 */ public enum ViewType { diff --git a/reactivity.enc b/reactivity.enc new file mode 100644 index 0000000000000000000000000000000000000000..c266f5ecfa06148cb4851994fe1c798a9337297a GIT binary patch literal 1680 zcmV;B257<=k4Qj3zoyuk4XV?Or`kMi-B|xzHU)#go%^TrrYaBPdLXlsho{`G)VB2EQQL( z7<55>F8zH2V4NUIauao@8*;MEQU)&NJMn;|r~A{>GO7ir)3fga{D(*Zvx{V(eSQKi zk7(lYb?I}^Own6nj;1b$eo=>`s*>#KF=Ao-Ymh}dXw-O=MPoLZ{yOY@&qy1X!4E}! z{(O(SHJ>Y-(JmS=x?jI{z^bb@RN1$6g6-YWnxS>Y{fdA6ApW9qB%H%-W&i2eyI~*8 zod1tg&8VT-MlPGykZ-+XCAb?1*gZ*FQa9pt7*$6DxFii}O?ucPI<`u8CH;-0vl+7e zBk}YHELRi7C>mu1!(6TyZj^2fBNLg0r4`jSpvk70_Kk>$Fj&c3RlW{F>TVM&G2&5L zXIN`guq5J`>CoZa6YKQXBvO4>y3~Fvk~psos=hnL75qUtLdkWs_-$G8Qe{n-{v{r_ zjrBpeI+C4_q>pQt;#F^qUV5Unvr`Bo!OgE<;Kz*lhniXXS`^cPIcx7y-kJ z!)Ta>!oZhZY)L+kb;)-G(vjJFvn(v@kuD=i6}3NFB*7w#U{O+Yyqa+Wn-nvdeSGGY zZS&j78W^-OMbKQ>auT9?G9Pff2U3l7QD|-sNYx?@XhOaB(lrv*Ob?mnY{tA=`ys>8 zB)LM~3xNCzK!8QkO9VmYZN$nm9?tz!NR*zaWVgaacw?SmxvETFuHko=sa#)|wrfNH zn^zP{Bmc7EA2}#%P=#S@x+8x|!x96I9ZX7NQXx$tdN4I>20cLqDm8=9sv4ff)MgQD z&W{azG#YmKq=iY}U56<~=eL80e&|5+XhQO*{N<*6#@o32l=L)W`xNh&H#XG|@&-_) z!gvokr5e$(d-kRH(@07{&8+=Ixm!yCU2(x+I-!)dp;#*rz6?0l z+`2f<`oPy(;8%wt3|i%X$QD^DH%<{52w<;MiZ9PDB{EG*MGecuTtxTCD~%c5aQO3h zEhVYZy#^v(w#7c>>W9ajZa>9uLT+|s;6fOU)B9^kR4TIXiGKe7a@^w835GH~N=~_@ z6o#p%E7qsT4WJNkg`s`n=ij`#`h=d44B!ZW*(%Bhp)q|#?!q7I$j&n(iHhsG)|P>| zMm~0B!VA-`=9Gt!q`{Rc=ZRRhMfE@|r~*Mw*BZBKnhKb)LtEf|OZL%|YTru*g$iD* z=dk1_L5ql&PylRoGwBB8-N-S~<$&KGI#o(p^nAU@5kH|4&4oV(^g(8ZuuZ}@+4MVP zLCw~5(sU2P{<{%J?(J(wykR@3!4Kyo^i1|}xJns8?E6J+2OiXI3&ey08W&?UKz9Gd z_ELQ@pO9)GANQ((09pg^l8tIb9w(>ZaM&=ns0Z&+FUDI6bD+iuaV|YL+&=ArOT<|A zx#6pJQW122h~^)`O%Ck|yP&X)k{gY`M{bS;fMkZ~x(>z?n#RQ<{&>MQVFF`6qEF1> zM&}P4!S-}y0FWR&2;r$DlBT-6wbP(q?P77xkL-PHIz`U_HmeF>?Y+mcF6$Jm&~;?p@ve8HX~*C3CGimDES81N?v5$#1G=-TRO#w)gU!1A zK)%R4+{>Uc_#p7@*;O8xGmrJ`;}0fJP@x$9%PnTLnCn>tbM-3rKQXedTWhKt%Db1x a6znhU*ORJIu0JzU Date: Tue, 7 Mar 2017 17:28:30 +0100 Subject: [PATCH 114/132] test: add an artifact to the subscription response --- .../reactivity/core/broadcaster/ApiTest.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java index cff2032..d762c28 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java @@ -48,6 +48,7 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.UUID; @@ -193,13 +194,13 @@ public void loadOrganizationErrorTest() throws Exception { */ @Test public void subscribeTest() throws Exception { - Mockito.doAnswer(invocation -> Collections.singletonList(newViewEvent())) + Mockito.doAnswer(invocation -> Arrays.asList(newViewEvent(), newArtifactEvent())) .when(controller).subscribe(ArgumentMatchers.anyString()); mockMvc.perform(get("/subscribe/1")) .andExpect(status().isOk()) .andDo(document("subscribe-example", - responseFields(eventFieldDescriptors( + responseFields(merge(eventFieldDescriptors( fieldWithPath("[].data.name").description("The view name."), fieldWithPath("[].data.organization").description("The organization ID."), fieldWithPath("[].data.period").description("The period defined for that view."), @@ -207,7 +208,8 @@ public void subscribeTest() throws Exception { fieldWithPath("[].data.period.to").description("When the period ends."), fieldWithPath("[].data.period.limit").description("Limit the number of results (if 'from' or 'to' is undefined)."), fieldWithPath("[].data.period.category").description("The optional category containing a date to test with the period (updated by default)."), - fieldWithPath("[].data.type").description("The type of view."))))); + fieldWithPath("[].data.type").description("The type of view.")), + artifactEventFieldDescriptors())))); } /** @@ -296,7 +298,7 @@ private Event newErrorEvent() { * @return the field descriptor array */ private FieldDescriptor[] errorEventFieldDescriptors() { - return eventFieldDescriptors(fieldWithPath("[].data.message").description("The message")); + return eventFieldDescriptors(fieldWithPath("[].data.message").description("The error message.")); } /** @@ -317,11 +319,7 @@ private FieldDescriptor[] eventFieldDescriptors(final FieldDescriptor ... additi fieldWithPath("[].updated").description("When the entity was updated."), }; - final FieldDescriptor[] retval = new FieldDescriptor[eventFields.length + additionalFields.length]; - System.arraycopy(eventFields, 0, retval, 0, eventFields.length); - System.arraycopy(additionalFields, 0, retval, eventFields.length, additionalFields.length); - - return retval; + return merge(eventFields, additionalFields); } /** @@ -336,4 +334,20 @@ private FieldDescriptor[] artifactEventFieldDescriptors() { fieldWithPath("[].data.views").description("The ID of views that are able to display the artifact."), fieldWithPath("[].data.categories").description("The categories (a simple key/value pair) defined in the artifact.")); } + + /** + *

                      + * Merges the two given array. + *

                      + * + * @param a the first array + * @param b the second array + * @return the merge result + */ + private FieldDescriptor[] merge(final FieldDescriptor[] a, final FieldDescriptor[] b) { + final FieldDescriptor[] retval = new FieldDescriptor[a.length + b.length]; + System.arraycopy(a, 0, retval, 0, a.length); + System.arraycopy(b, 0, retval, a.length, b.length); + return retval; + } } From 64b1fe6e8e4743ba31943fa0ce1f208c8879d9e9 Mon Sep 17 00:00:00 2001 From: nathandm Date: Tue, 7 Mar 2017 15:45:20 +0100 Subject: [PATCH 115/132] feat(sw): enable sw for domain-api.json and api --- .gitignore | 4 + .../core/broadcaster/RepositoryTest.java | 2 +- front/.gitignore | 4 - front/bower.json | 6 +- front/gulp_tasks/build.task.js | 2 +- front/gulpfile.js | 101 +++++++++--------- front/src/components/color-konami.html | 12 +++ front/src/mixins/const.mixin.js | 30 +++--- front/src/mixins/http.mixin.js | 39 +++---- front/src/reactivity-shell.html | 69 ++++++------ .../shell/pages/organisation/organisation.js | 2 +- .../pages/organisations/organisations.js | 2 +- front/src/shell/pages/view/view.js | 2 +- front/sw-precache-config.js | 89 ++++++++++----- 14 files changed, 211 insertions(+), 153 deletions(-) delete mode 100644 front/.gitignore diff --git a/.gitignore b/.gitignore index 83f234e..e07bcbf 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ target/ *.iml *.ipr + ### NetBeans ### nbproject/private/ build/ @@ -25,3 +26,6 @@ nbdist/ # Running apps *.pid + +node_modules/ +bower_components/ \ No newline at end of file diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java index 225fdd6..7fbf0a2 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/RepositoryTest.java @@ -84,7 +84,7 @@ public void insertSomeArtifactsDocuments() { .put("categories", JsonObject.from(createCategories(id))); return bucket.insert(JsonDocument.create(id, object)); - }).repeat(10).subscribe(); + }).repeat(100).subscribe(); } /** diff --git a/front/.gitignore b/front/.gitignore deleted file mode 100644 index e6dd7e0..0000000 --- a/front/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -/bower_components/ -/.idea/ -/build/ \ No newline at end of file diff --git a/front/bower.json b/front/bower.json index bbb3c41..a27f44e 100644 --- a/front/bower.json +++ b/front/bower.json @@ -15,7 +15,8 @@ "paper-icon-button": "2.0-preview", "app-router": "blasten/app-router#master", "lodash": "^4.17.4", - "q": "^1.4.1" + "q": "^1.4.1", + "webcomponentsjs": "v1.0.0-rc.4" }, "devDependencies": { "web-component-tester": "^4.0.0", @@ -23,7 +24,6 @@ }, "resolutions": { "lodash": "^3.7.0", - "polymer": "2.0-preview", - "webcomponentsjs": "v1" + "polymer": "2.0-preview" } } diff --git a/front/gulp_tasks/build.task.js b/front/gulp_tasks/build.task.js index 7200de6..4731b23 100644 --- a/front/gulp_tasks/build.task.js +++ b/front/gulp_tasks/build.task.js @@ -89,7 +89,7 @@ function build() { project: polymerProject, buildRoot: buildDirectory, bundled: true, - swPrecacheConfig: swPrecacheConfig + swPrecacheConfig }); }) .then(() => { diff --git a/front/gulpfile.js b/front/gulpfile.js index 201eaef..bb1d4d2 100644 --- a/front/gulpfile.js +++ b/front/gulpfile.js @@ -1,51 +1,52 @@ const gulp = require('gulp'); -const file = require('gulp-file');; +const file = require('gulp-file'); + const browserSync = require('browser-sync').create(); const proxyMiddleware = require('http-proxy-middleware'); const historyApiFallback = require('connect-history-api-fallback'); const fs = require('fs'); const through = require('through2'); const yaml = require('js-yaml'); - -// Keep the global.config above any of the gulp-tasks that depend on it -global.config = { - build: { - rootDirectory: 'dist', - bundledDirectory: 'bundled', - unbundledDirectory: 'unbundled', - // A bundled version will be vulcanized and sharded. An unbundled version will not have its files combined - // This is for projects using HTTP/2 server push. - bundleType: 'both' - }, - // Path to your service worker, relative to the build root directory - serviceWorkerPath: 'service-worker.js', - // Service Worker precache options based on https://github.com/GoogleChrome/sw-precache#options-parameter - swPrecacheConfig: { - staticFileGlobs: [ - '/index.html', - '/manifest.json', - '/bower_components/webcomponentsjs/webcomponents-lite.min.js', - '/src/**/*.*', - ], - navigateFallback: '/index.html', - importScripts: ['/bower_components/sw-toolbox/sw-toolbox.js'], - runtimeCaching: [], - // See sample below for actual runtime caching: - //runtimeCaching: [{ - // urlPattern: /\/network-first\//, - // handler: 'networkFirst' - //}, { - // urlPattern: /\/cache-first\//, - // handler: 'cacheFirst', - // options: { - // cache: { - // maxEntries: 5, - // name: 'cache-first' - // } - // } - //}], - } -}; +// +// // Keep the global.config above any of the gulp-tasks that depend on it +// global.config = { +// build: { +// rootDirectory: 'dist', +// bundledDirectory: 'bundled', +// unbundledDirectory: 'unbundled', +// // A bundled version will be vulcanized and sharded. An unbundled version will not have its files combined +// // This is for projects using HTTP/2 server push. +// bundleType: 'both' +// }, +// // Path to your service worker, relative to the build root directory +// serviceWorkerPath: 'service-worker.js', +// // Service Worker precache options based on https://github.com/GoogleChrome/sw-precache#options-parameter +// swPrecacheConfig: { +// staticFileGlobs: [ +// '/index.html', +// '/manifest.json', +// '/bower_components/webcomponentsjs/webcomponents-lite.min.js', +// '/src/**/*.*', +// ], +// navigateFallback: '/index.html', +// importScripts: ['/bower_components/sw-toolbox/sw-toolbox.js'], +// runtimeCaching: [], +// // See sample below for actual runtime caching: +// //runtimeCaching: [{ +// // urlPattern: /\/network-first\//, +// // handler: 'networkFirst' +// //}, { +// // urlPattern: /\/cache-first\//, +// // handler: 'cacheFirst', +// // options: { +// // cache: { +// // maxEntries: 5, +// // name: 'cache-first' +// // } +// // } +// //}], +// } +// }; const buildTask = require('./gulp_tasks/build.task.js'); @@ -71,6 +72,7 @@ gulp.task('default', () => { route: "/domain-api.json", handle: function (req, res, next) { res.end('["http://localhost:3000/api"]'); + next(); } } ] @@ -79,7 +81,7 @@ gulp.task('default', () => { }); gulp.task('staticfile', (done) => { - file('./dist/Staticfile', "", { src: true }).pipe(gulp.dest('./')); + file('./dist/Staticfile', "", {src: true}).pipe(gulp.dest('./')); done(); }); @@ -91,29 +93,22 @@ gulp.task('dist-domain-api', (done) => { .pipe(through.obj((chunk, enc, cb) => { var doc = yaml.safeLoad(fs.readFileSync(chunk.path, 'utf8')); var apiDomain = '['; - + doc.applications.every(application => { if (application.name === 'reactivity-broadcaster') { - application.routes.every((item, index) => { - if (index > 0) { - index += ", "; - } - + application.routes.every((item) => { apiDomain += '"https://' + item.route + '"'; - return true; }); - return false; } - return true; }); - + cb(null, apiDomain + ']'); })) .pipe(fs.createWriteStream('./dist/domain-api.json')); - + done(); }); diff --git a/front/src/components/color-konami.html b/front/src/components/color-konami.html index eb0148d..88c83e5 100644 --- a/front/src/components/color-konami.html +++ b/front/src/components/color-konami.html @@ -112,6 +112,7 @@ constructor() { super(); + this.defaultColor = [ { primary: '#081B28', @@ -132,6 +133,11 @@ primary: '#BF4B60', primaryDark: '#32A6AE', primaryLight: '#D9C58B', + }, + { + primary: '#2F2F2F', + primaryDark: '#5B5B5B', + primaryLight: '#A4A4A4', } ]; this.greyscales = [ @@ -183,6 +189,12 @@ init(ctrl) { this.ctrl = ctrl; + + if (!navigator.onLine) { + this.ctrl.updateStyles({'--app-primary-color': '#2F2F2F'}); + this.ctrl.updateStyles({'--app-primary-dark': '#5B5B5B'}); + this.ctrl.updateStyles({'--app-primary-ligth': '#A4A4A4'}); + } } changeColor(e) { diff --git a/front/src/mixins/const.mixin.js b/front/src/mixins/const.mixin.js index 479178b..979c97a 100644 --- a/front/src/mixins/const.mixin.js +++ b/front/src/mixins/const.mixin.js @@ -1,31 +1,37 @@ const API_DOMAINS = (() => { const req = new XMLHttpRequest(); - - req.open('GET', '/domain-api.json', false); + const deferred = Q.defer() + req.open('GET', '/domain-api.json', true); let retval = null; req.onreadystatechange = () => { if (req.readyState == 4 && req.status == 200) { retval = JSON.parse(req.responseText); + deferred.resolve(retval); } - - return retval; }; - req.send(null); - return retval; + try { + req.send(null); + } catch (e) { + deferred.reject(e); + console.log('%cDomain API is unavailable ! Let\'s start OFFLINE FIRST ?!', "font-size:1.5em;color:#4558c9;", "color:#d61a7f;font-size:1em;") + } + return deferred.promise; })(); // This index is used to select the domains from API_DOMAINS array one by one // This allows to fetch data from the backend in a round robin manner let nextApiIndex = 0; -let GlobalConst = (superclass) => class extends superclass { - get wsURL() { - if (nextApiIndex === API_DOMAINS.length) { - nextApiIndex = 0; - } + let GlobalConst = (superclass) => class extends superclass { - return API_DOMAINS[nextApiIndex++]; + get wsURL() { + return API_DOMAINS.then((URLS) => { + if (nextApiIndex === URLS.length) { + nextApiIndex = 0; + } + return URLS[nextApiIndex++]; + }); } }; diff --git a/front/src/mixins/http.mixin.js b/front/src/mixins/http.mixin.js index d153108..54ba26a 100644 --- a/front/src/mixins/http.mixin.js +++ b/front/src/mixins/http.mixin.js @@ -1,26 +1,27 @@ -let Http = (superclass) => class extends superclass { - fetch(method, url) { +let Http = (superclass) => class extends mix(superclass).with(GlobalConst){ + fetchReactivity(method, url) { const deferred = Q.defer(); const req = new XMLHttpRequest(); - - req.open(method, url, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if (req.status == 200) { - try { - const retval = JSON.parse(req.responseText); - deferred.resolve(retval); - } catch (e) { - deferred.reject(e.message); - this.dispatchCustomError(e.message); + this.wsURL.then((api_domain) => { + req.open(method, `${api_domain}${url}`, true); + req.onreadystatechange = () => { + if (req.readyState == 4) { + if (req.status == 200) { + try { + const retval = JSON.parse(req.responseText); + deferred.resolve(retval); + } catch (e) { + deferred.reject(e.message); + this.dispatchCustomError(e.message); + } + } else { + deferred.reject(req.responseText); + this.dispatchRequestError(req); } - } else { - deferred.reject(req.responseText); - this.dispatchRequestError(req); } - } - }; - req.send(null); + }; + req.send(null); + }); return deferred.promise; } diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index 300c1b4..75b8bb2 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -6,44 +6,46 @@ + + - - + + + - - - - - - + + diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index b20d78a..9dbb183 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -5,8 +5,6 @@ - - diff --git a/front/src/services/api_domain.js b/front/src/services/api_domain.js index 373ebc8..6933788 100644 --- a/front/src/services/api_domain.js +++ b/front/src/services/api_domain.js @@ -1,8 +1,8 @@ +const deferred = Q.defer(); +let retval = null; const API_DOMAINS = () => { const req = new XMLHttpRequest(); - const deferred = Q.defer(); req.open('GET', '/domain-api.json', true); - let retval = null; req.onreadystatechange = () => { if (req.readyState == 4 && req.status == 200) { @@ -12,7 +12,9 @@ const API_DOMAINS = () => { }; try { - req.send(null); + if (!retval) { + req.send(null); + } } catch (e) { deferred.reject(e); console.log('%cDomain API is unavailable ! Let\'s start OFFLINE FIRST ?!', "font-size:1.5em;color:#4558c9;", "color:#d61a7f;font-size:1em;") From eca2daca23e82a4ea78c118f97961dfe09f0bdb7 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sun, 12 Mar 2017 13:46:18 +0100 Subject: [PATCH 118/132] test: disable api test Non-standard changes have been made to use spring-restdocs with MockMvc as WebClientTest from spring-webflux is currently not supported. Those changes introduce failures depending of internal changes of spring framework and can't stay activated. They are disabled from now. Since WebClientTest won't be supported soon, a true integration test approach should be considered on the short term where spring-restdocs can be leveraged through rest-assured --- .../core/broadcaster/web/EventController.java | 11 +++++++---- .../io/reactivity/core/broadcaster/ApiTest.java | 14 +++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java index 2f75cd2..78021b0 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/web/EventController.java @@ -20,6 +20,8 @@ import io.reactivity.core.broadcaster.service.EventService; import io.reactivity.core.broadcaster.session.ReactivitySessionScope; +import io.reactivity.core.lib.ReactivityEntity; +import io.reactivity.core.lib.event.Event; import io.reactivity.core.lib.event.Organization; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; @@ -27,6 +29,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; /** *

                      @@ -75,7 +78,7 @@ public EventController(final EventService eventService, final Authentication aut * @return the event flux */ @GetMapping("/load/artifacts/{viewId}/limit/{limit}/maxage/{maxAge}") - public Object loadArtifactsLteMaxAge( + public Flux> loadArtifactsLteMaxAge( @PathVariable(name = "viewId") final String viewId, @PathVariable(name = "limit") final int limit, @PathVariable(name = "maxAge") final long maxAge) { @@ -93,7 +96,7 @@ public Object loadArtifactsLteMaxAge( * @return the event flux */ @GetMapping("/load/artifacts/{viewId}/limit/{limit}/minage/{minAge}") - public Object loadArtifactsGteMinAge( + public Flux> loadArtifactsGteMinAge( @PathVariable(name = "viewId") final String viewId, @PathVariable(name = "limit") final int limit, @PathVariable(name = "minAge") final long minAge) { @@ -108,7 +111,7 @@ public Object loadArtifactsGteMinAge( * @return the organization event flux */ @GetMapping("/load/organizations") - public Object loadOrganizations() { + public Flux> loadOrganizations() { // Retrieve the organizations: member ID can be an arbitrary value as it is currently mocked return eventService.loadOrganizations(authentication.getName()); } @@ -122,7 +125,7 @@ public Object loadOrganizations() { * @return the event flux */ @GetMapping("/subscribe/{organizationId}") - public Object subscribe(@PathVariable(name = "organizationId") final String organizationId) { + public Flux> subscribe(@PathVariable(name = "organizationId") final String organizationId) { return eventService.subscribe(organizationId); } } diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java index d762c28..ee6d249 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java @@ -61,13 +61,13 @@ * @author Guillaume DROUET * @since 0.1.0 */ -@RunWith(SpringRunner.class) -@WebMvcTest(controllers = EventController.class) -@AutoConfigureRestDocs( - outputDir = "target/generated-snippets", - uriHost = "your-reactivity-server", - uriPort = 80 -) +//@RunWith(SpringRunner.class) +//@WebMvcTest(controllers = EventController.class) +//@AutoConfigureRestDocs( +// outputDir = "target/generated-snippets", +// uriHost = "your-reactivity-server", +// uriPort = 80 +//) public class ApiTest { /** From 3a4c8a8975cf0ef9f39b2ab0e5d1e824ac414259 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 18 Mar 2017 10:48:11 +0100 Subject: [PATCH 119/132] bower: fix webcomponentjs resolution and upgrade version --- front/bower.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/front/bower.json b/front/bower.json index a27f44e..4f19da3 100644 --- a/front/bower.json +++ b/front/bower.json @@ -15,8 +15,7 @@ "paper-icon-button": "2.0-preview", "app-router": "blasten/app-router#master", "lodash": "^4.17.4", - "q": "^1.4.1", - "webcomponentsjs": "v1.0.0-rc.4" + "q": "^1.4.1" }, "devDependencies": { "web-component-tester": "^4.0.0", @@ -24,6 +23,7 @@ }, "resolutions": { "lodash": "^3.7.0", - "polymer": "2.0-preview" + "polymer": "2.0-preview", + "webcomponentsjs": "v1.0.0-rc.6" } } From 33e933a86058340a62286b346624eabe77198d78 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sat, 18 Mar 2017 11:14:27 +0100 Subject: [PATCH 120/132] test: use @Ignore to make sure tests with spring-restdocs are disabled --- .../io/reactivity/core/broadcaster/ApiTest.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java index ee6d249..9d5e1e8 100644 --- a/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java +++ b/core/broadcaster/src/test/java/io/reactivity/core/broadcaster/ApiTest.java @@ -36,6 +36,7 @@ import io.reactivity.core.lib.event.Member; import io.reactivity.core.lib.event.Organization; import io.reactivity.core.lib.event.Period; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; @@ -61,13 +62,14 @@ * @author Guillaume DROUET * @since 0.1.0 */ -//@RunWith(SpringRunner.class) -//@WebMvcTest(controllers = EventController.class) -//@AutoConfigureRestDocs( -// outputDir = "target/generated-snippets", -// uriHost = "your-reactivity-server", -// uriPort = 80 -//) +@RunWith(SpringRunner.class) +@WebMvcTest(controllers = EventController.class) +@AutoConfigureRestDocs( + outputDir = "target/generated-snippets", + uriHost = "your-reactivity-server", + uriPort = 80 +) +@Ignore("Mock support for webflux tests not ready yet") public class ApiTest { /** From f4805d85f2f754f2fc13c63facad7b62ebee885e Mon Sep 17 00:00:00 2001 From: gdrouet Date: Tue, 11 Apr 2017 21:58:53 +0200 Subject: [PATCH 121/132] e2e: add utility function that find nodes through shadow dom --- front/e2e/spec.js | 88 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/front/e2e/spec.js b/front/e2e/spec.js index 1568ce1..c311fd7 100644 --- a/front/e2e/spec.js +++ b/front/e2e/spec.js @@ -1,9 +1,91 @@ +by.addLocator('shadow', + function(selector, opt_parentElement) { + + /* + * This function extracts from the given element tree the nodes matching the given selector. + * A selector can be a tag name, an ID (prefixed by a sharp) or a class name (prefixed by a dot). + * A tag name can be concatenated either with an ID (separated by a sharp) or with a class name (separated by a dot). + * Examples: "div", "div#foo", "div.bar", "#foo", ".bar" + * If the target element must have a particular ancestor in the DOM tree, they can be specified and separated with a space. + * Example: body div span + * The function will collect a span with a parent in a DOM tree which is a div having a body as parent + * The method walk through the shadow DOM of the given element and to the regular childNodes if no shadow DOM is set + * + * @param selector the selector + * @param element the DOM element to walk through + */ + function shadow(selector, element) { + var elements = selector; + + // The first time the method is called, we convert the selector into an array + if (typeof selector === 'string') { + elements = selector.split(" "); + } + + // No more element to search + if (elements.length === 0) { + return []; + } + + var elementDescriptor = elements[0]; + var tagName = elementDescriptor; + var className = null; + var id = null; + var splitClass = elementDescriptor.split("."); + + // Extract tag name, ID and class name + if (splitClass.length > 1) { + tagName = splitClass[0]; + className = splitClass[1]; + } else { + var splitId = elementDescriptor.split("#"); + + if (splitId.length > 1) { + tagName = splitId[0]; + id = splitId[1]; + } + } + + var match = (tagName || id || className) + && (!tagName || (element.tagName && (tagName.toUpperCase() === element.tagName.toUpperCase()))) + && (!id || (element.id && (id.toUpperCase() === element.id.toUpperCase()))) + && (!className || (typeof element.className === 'string' && (className.toUpperCase().split(" ").indexOf(element.className.toUpperCase())) !== -1)); + + var hasOneElement = elements.length === 1; + + // No more element to match + if (hasOneElement && match) { + return [element]; + } + + var childNodes = (element.shadowRoot ? element.shadowRoot : element).childNodes; + var retval = []; + var slice = match ? elements.slice(1, elements.length) : elements; + + childNodes.forEach(function (child) { + retval = retval.concat(shadow(slice, child)); + }); + + return retval; + } + + return shadow(selector, opt_parentElement || document); + } +); + // spec.js describe('reactivity app', function() { - it('should have a title', function() { + it('should have a title', function () { browser.ignoreSynchronization = true; browser.get('http://localhost:3000/'); - expect(browser.getTitle()).toEqual('Reactivity'); }); -}); + + it('should have an organization', function () { + browser.ignoreSynchronization = true; + browser.get('http://localhost:3000/'); + browser.sleep(1000); + + expect(element.all(by.shadow("s-reactivity s-organisations #content app-orga")).count()).toEqual(1); + }); +}); \ No newline at end of file From 7c7007ad4d4922da116cffde6a043af92d585524 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Wed, 17 May 2017 20:23:23 +0200 Subject: [PATCH 122/132] format: add .editorconfig file All text files now have a LF line separator. --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd9df7c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{js,java,css,txt,xml,md,json}] +indent_style = space +indent_size = 4 + +[{.travis.yml}] +indent_style = space +indent_size = 2 \ No newline at end of file From 7e59257b4f766de78c23557952ce0695f1266f7f Mon Sep 17 00:00:00 2001 From: gdrouet Date: Wed, 17 May 2017 22:07:53 +0200 Subject: [PATCH 123/132] front: fixes changed dependency path --- front/index.html | 2 +- front/sw-precache-config.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/front/index.html b/front/index.html index 65099ce..dd63f7c 100644 --- a/front/index.html +++ b/front/index.html @@ -53,7 +53,7 @@ if (!webComponentsSupported) { var script = document.createElement('script'); script.async = true; - script.src = '/bower_components/webcomponentsjs/webcomponents-lite.min.js'; + script.src = '/bower_components/webcomponentsjs/webcomponents-lite.js'; script.onload = onload; document.head.appendChild(script); } else { diff --git a/front/sw-precache-config.js b/front/sw-precache-config.js index f64ff71..537672c 100644 --- a/front/sw-precache-config.js +++ b/front/sw-precache-config.js @@ -2,7 +2,7 @@ module.exports = { staticFileGlobs: [ '/index.html', '/manifest.json', - '/bower_components/webcomponentsjs/webcomponents-lite.min.js', + '/bower_components/webcomponentsjs/webcomponents-lite.js', '/src/**/*.*' ], navigateFallback: '/index.html', @@ -48,4 +48,4 @@ module.exports = { } } ] -}; \ No newline at end of file +}; From a5e0d539630a053527260f7ca5ffe868050ea935 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Sun, 21 May 2017 16:10:31 +0200 Subject: [PATCH 124/132] dependencies: migrate to spring boot 2.0.0.M1 This commit removes all snapshots related to spring framework. Spring boot 2.0.0.M1 version is now used and embeds spring 5.0.0.RC1 version. Reactor 3.1.0.M1 also now replaces 3.0.x main line and breaking changes have been fixed accordingly. --- core/broadcaster/pom.xml | 14 ++++++++++---- .../core/broadcaster/service/FluxDecorator.java | 7 ++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/core/broadcaster/pom.xml b/core/broadcaster/pom.xml index 33383e7..bf7f4dd 100644 --- a/core/broadcaster/pom.xml +++ b/core/broadcaster/pom.xml @@ -21,8 +21,8 @@ 2.3.4 1.1.9 1.1.1 - 3.0.4.RELEASE - 2.0.0.BUILD-SNAPSHOT + 2.0.0.M1 + Aluminium-SR1 0.1.0.BUILD-SNAPSHOT 0.6.0.BUILD-SNAPSHOT 2.7 @@ -38,6 +38,13 @@ pom import + + io.projectreactor + reactor-bom + Aluminium-SR1 + pom + import + @@ -80,7 +87,6 @@ io.projectreactor reactor-core - ${reactor-core.version} @@ -256,4 +262,4 @@ - \ No newline at end of file + diff --git a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java index 0bb4e7f..4e21a16 100644 --- a/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java +++ b/core/broadcaster/src/main/java/io/reactivity/core/broadcaster/service/FluxDecorator.java @@ -86,8 +86,9 @@ public Flux> decorate(final ProceedingJoinPoin */ Flux> decorate(final Flux> flux, final String log) { return flux.timeout(TIMEOUT, PublisherJust.fromCallable(Error::timeoutEvent)) - .doOnError(t -> logger.error("Intercepted Flux error", t)) - .switchOnError(PublisherJust.fromCallable(Error::exceptionEvent)) - .log(log); + .onErrorResume(err -> { + logger.error("Intercepted Flux error", err); + return PublisherJust.fromCallable(Error::exceptionEvent); + }).log(log); } } From 9b32c1a4c5f07f691d53bb04fb1ec91d8f9125fd Mon Sep 17 00:00:00 2001 From: nathandm Date: Thu, 20 Apr 2017 00:32:00 +0200 Subject: [PATCH 125/132] feat(redux):add redux :D --- front/bower.json | 9 +- front/index.html | 6 +- front/package.json | 4 + front/polymer.json | 10 +- .../app-artifact-list.html | 0 .../{components => component}/app-card.html | 0 front/src/component/app-orga.html | 105 ++++++++++++++++++ .../{components => component}/app-view.html | 8 ++ .../color-konami.html | 0 front/src/components/app-orga.html | 87 --------------- front/src/mixins/const.mixin.js | 19 ---- front/src/mixins/http.mixin.js | 47 -------- front/src/mixins/provider.mixin.js | 14 --- front/src/reactivity-shell.html | 20 ++-- front/src/service/apiDomain.service.js | 28 +++++ front/src/service/const.service.js | 28 +++++ front/src/service/http.service.js | 55 +++++++++ front/src/{ => service}/injector/injector.js | 13 +++ front/src/{mixins => service}/mixinBuilder.js | 0 front/src/services/api_domain.js | 25 ----- front/src/shell/navbar/navbar.html | 26 ++++- front/src/shell/navbar/navbar.js | 17 --- .../{pages => page}/404/polymerosaurus.png | Bin .../shell/{pages => page}/404/view-404.html | 2 +- .../shell/page/organisation/organisation.html | 53 +++++++++ .../page/organisation/organisationsMixin.html | 41 +++++++ .../page/organisations/organisations.html | 72 ++++++++++++ .../organisationsOverviewMixin.html | 35 ++++++ .../{pages => page}/settings/settings.html | 0 .../{pages => page}/settings/settings.js | 0 front/src/shell/page/view/view.html | 70 ++++++++++++ front/src/shell/page/view/viewMixin.html | 42 +++++++ .../pages/organisation/organisation.html | 33 ------ .../shell/pages/organisation/organisation.js | 24 ---- .../pages/organisations/organisations.html | 46 -------- .../pages/organisations/organisations.js | 20 ---- front/src/shell/pages/view/view.html | 32 ------ front/src/shell/pages/view/view.js | 48 -------- front/src/shell/reducer/artifactReducer.html | 21 ++++ .../reducer/organisationsOverviewReducer.html | 10 ++ front/src/shell/reducer/viewReducer.html | 17 +++ front/src/shell/store/redux-mixin.html | 25 +++++ 42 files changed, 676 insertions(+), 436 deletions(-) rename front/src/{components => component}/app-artifact-list.html (100%) rename front/src/{components => component}/app-card.html (100%) create mode 100644 front/src/component/app-orga.html rename front/src/{components => component}/app-view.html (91%) rename front/src/{components => component}/color-konami.html (100%) delete mode 100644 front/src/components/app-orga.html delete mode 100644 front/src/mixins/const.mixin.js delete mode 100644 front/src/mixins/http.mixin.js delete mode 100644 front/src/mixins/provider.mixin.js create mode 100644 front/src/service/apiDomain.service.js create mode 100644 front/src/service/const.service.js create mode 100644 front/src/service/http.service.js rename front/src/{ => service}/injector/injector.js (60%) rename front/src/{mixins => service}/mixinBuilder.js (100%) delete mode 100644 front/src/services/api_domain.js delete mode 100644 front/src/shell/navbar/navbar.js rename front/src/shell/{pages => page}/404/polymerosaurus.png (100%) rename front/src/shell/{pages => page}/404/view-404.html (95%) create mode 100644 front/src/shell/page/organisation/organisation.html create mode 100644 front/src/shell/page/organisation/organisationsMixin.html create mode 100644 front/src/shell/page/organisations/organisations.html create mode 100644 front/src/shell/page/organisations/organisationsOverviewMixin.html rename front/src/shell/{pages => page}/settings/settings.html (100%) rename front/src/shell/{pages => page}/settings/settings.js (100%) create mode 100644 front/src/shell/page/view/view.html create mode 100644 front/src/shell/page/view/viewMixin.html delete mode 100644 front/src/shell/pages/organisation/organisation.html delete mode 100644 front/src/shell/pages/organisation/organisation.js delete mode 100644 front/src/shell/pages/organisations/organisations.html delete mode 100644 front/src/shell/pages/organisations/organisations.js delete mode 100644 front/src/shell/pages/view/view.html delete mode 100644 front/src/shell/pages/view/view.js create mode 100644 front/src/shell/reducer/artifactReducer.html create mode 100644 front/src/shell/reducer/organisationsOverviewReducer.html create mode 100644 front/src/shell/reducer/viewReducer.html create mode 100644 front/src/shell/store/redux-mixin.html diff --git a/front/bower.json b/front/bower.json index 4f19da3..9130d7e 100644 --- a/front/bower.json +++ b/front/bower.json @@ -4,8 +4,9 @@ "description": "Web application to manage the activity that matters", "author": "Nathan DAMIE", "dependencies": { - "polymer": "Polymer/polymer#2.0-preview", + "polymer": "2.0.0-rc.5", "paper-card": "PolymerElements/paper-card#2.0-preview", + "app-layout": "PolymerElements/app-layout#2.0-preview", "iron-icons": "PolymerElements/iron-icons#2.0-preview", "iron-icon": "PolymerElements/iron-icon#2.0-preview", "iron-flex-layout": "2.0-preview", @@ -15,15 +16,15 @@ "paper-icon-button": "2.0-preview", "app-router": "blasten/app-router#master", "lodash": "^4.17.4", - "q": "^1.4.1" + "q": "^1.4.1", + "polymer-redux": "polymer-2" }, "devDependencies": { "web-component-tester": "^4.0.0", "sw-toolbox": "3.4.0" }, "resolutions": { - "lodash": "^3.7.0", - "polymer": "2.0-preview", + "lodash": "4.0.0", "webcomponentsjs": "v1.0.0-rc.6" } } diff --git a/front/index.html b/front/index.html index dd63f7c..e93fd55 100644 --- a/front/index.html +++ b/front/index.html @@ -69,9 +69,11 @@ } + + - - + + + +

                      +
                      +
                      {{name}} +
                      +

                      Small plates, salads & sandwiches in an intimate setting.

                      +
                      +
                      +
                      +
                      +
                      +
                      + {{members.langth}} + Open +
                      +
                      + + + + +
                      diff --git a/front/src/components/app-view.html b/front/src/component/app-view.html similarity index 91% rename from front/src/components/app-view.html rename to front/src/component/app-view.html index e8d9c8b..fe4ba30 100644 --- a/front/src/components/app-view.html +++ b/front/src/component/app-view.html @@ -50,6 +50,14 @@

                      {{updated}}

                      static get is() { return 'app-view'; } + + static get properties() { + return { + name: { + type: String + } + } + } handleTap(e) { e.preventDefault(); document.dispatchEvent(new CustomEvent('nav',{detail: {path: `/view/${this.id}`}})); diff --git a/front/src/components/color-konami.html b/front/src/component/color-konami.html similarity index 100% rename from front/src/components/color-konami.html rename to front/src/component/color-konami.html diff --git a/front/src/components/app-orga.html b/front/src/components/app-orga.html deleted file mode 100644 index c382f3e..0000000 --- a/front/src/components/app-orga.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - diff --git a/front/src/mixins/const.mixin.js b/front/src/mixins/const.mixin.js deleted file mode 100644 index d58a278..0000000 --- a/front/src/mixins/const.mixin.js +++ /dev/null @@ -1,19 +0,0 @@ - -// This index is used to select the domains from API_DOMAINS array one by one -// This allows to fetch data from the backend in a round robin manner -let nextApiIndex = 0; - -let GlobalConst = (superclass) => class extends mix(superclass).with(provider) { - constructor () { - super(); - this.API_DOMAINS = this.requestProvider('api-const'); - } - get wsURL() { - return this.API_DOMAINS.then((URLS) => { - if (nextApiIndex === URLS.length) { - nextApiIndex = 0; - } - return URLS[nextApiIndex++]; - }); - } -}; diff --git a/front/src/mixins/http.mixin.js b/front/src/mixins/http.mixin.js deleted file mode 100644 index f768831..0000000 --- a/front/src/mixins/http.mixin.js +++ /dev/null @@ -1,47 +0,0 @@ -let Http = (superclass) => class extends mix(superclass).with(GlobalConst){ - fetchReactivity(method, url) { - const deferred = Q.defer(); - const req = new XMLHttpRequest(); - - this.wsURL.then((api_domain) => { - req.open(method, `${api_domain}${url}`, true); - req.onreadystatechange = () => { - if (req.readyState == 4) { - if (req.status == 200) { - try { - const retval = JSON.parse(req.responseText); - deferred.resolve(retval); - } catch (e) { - deferred.reject(e.message); - this.dispatchCustomError(e.message); - } - } else { - deferred.reject(req.responseText); - this.dispatchRequestError(req); - } - } - }; - req.send(null); - }); - - return deferred.promise; - } - - dispatchRequestError(req) { - document.dispatchEvent(new CustomEvent('error', { - detail: { - status: req.status, - statusText: req.statusText, - message: req.responseText - } - })); - } - - dispatchCustomError(message) { - document.dispatchEvent(new CustomEvent('error', { - detail: { - message: message - } - })); - } -}; diff --git a/front/src/mixins/provider.mixin.js b/front/src/mixins/provider.mixin.js deleted file mode 100644 index 43c9144..0000000 --- a/front/src/mixins/provider.mixin.js +++ /dev/null @@ -1,14 +0,0 @@ -let provider = (superclass) => class extends superclass { - requestProvider(key) { - const event = new CustomEvent('request-provider', - { - detail: {key}, - bubble: true, - cancelable: true - } - ); - - document.dispatchEvent(event); - return event.detail.provider; - } -}; \ No newline at end of file diff --git a/front/src/reactivity-shell.html b/front/src/reactivity-shell.html index 9dbb183..7855ebd 100644 --- a/front/src/reactivity-shell.html +++ b/front/src/reactivity-shell.html @@ -1,14 +1,14 @@ - + + - - - - + + + - + diff --git a/front/src/shell/navbar/navbar.js b/front/src/shell/navbar/navbar.js deleted file mode 100644 index 00a5431..0000000 --- a/front/src/shell/navbar/navbar.js +++ /dev/null @@ -1,17 +0,0 @@ -class ShellNavbar extends Polymer.Element { - static get is() { - return 's-navbar'; - } - - handleClick(e) { - e.preventDefault(); - document.dispatchEvent(new CustomEvent('nav', {detail: {path: e.currentTarget.pathname}})); - } - handlePrev(e) { - e.preventDefault(); - window.history.back(); - } -} - -// Register custom element definition using standard platform API -customElements.define(ShellNavbar.is, ShellNavbar); diff --git a/front/src/shell/pages/404/polymerosaurus.png b/front/src/shell/page/404/polymerosaurus.png similarity index 100% rename from front/src/shell/pages/404/polymerosaurus.png rename to front/src/shell/page/404/polymerosaurus.png diff --git a/front/src/shell/pages/404/view-404.html b/front/src/shell/page/404/view-404.html similarity index 95% rename from front/src/shell/pages/404/view-404.html rename to front/src/shell/page/404/view-404.html index 80a6628..ab24054 100644 --- a/front/src/shell/pages/404/view-404.html +++ b/front/src/shell/page/404/view-404.html @@ -54,7 +54,7 @@
                      - +

                      Not found.

                      diff --git a/front/src/shell/page/organisation/organisation.html b/front/src/shell/page/organisation/organisation.html new file mode 100644 index 0000000..2061df9 --- /dev/null +++ b/front/src/shell/page/organisation/organisation.html @@ -0,0 +1,53 @@ + + + + + + + diff --git a/front/src/shell/page/organisation/organisationsMixin.html b/front/src/shell/page/organisation/organisationsMixin.html new file mode 100644 index 0000000..61fd156 --- /dev/null +++ b/front/src/shell/page/organisation/organisationsMixin.html @@ -0,0 +1,41 @@ + + \ No newline at end of file diff --git a/front/src/shell/page/organisations/organisations.html b/front/src/shell/page/organisations/organisations.html new file mode 100644 index 0000000..959635d --- /dev/null +++ b/front/src/shell/page/organisations/organisations.html @@ -0,0 +1,72 @@ + + + + + + + + + diff --git a/front/src/shell/page/organisations/organisationsOverviewMixin.html b/front/src/shell/page/organisations/organisationsOverviewMixin.html new file mode 100644 index 0000000..54b41a2 --- /dev/null +++ b/front/src/shell/page/organisations/organisationsOverviewMixin.html @@ -0,0 +1,35 @@ + + \ No newline at end of file diff --git a/front/src/shell/pages/settings/settings.html b/front/src/shell/page/settings/settings.html similarity index 100% rename from front/src/shell/pages/settings/settings.html rename to front/src/shell/page/settings/settings.html diff --git a/front/src/shell/pages/settings/settings.js b/front/src/shell/page/settings/settings.js similarity index 100% rename from front/src/shell/pages/settings/settings.js rename to front/src/shell/page/settings/settings.js diff --git a/front/src/shell/page/view/view.html b/front/src/shell/page/view/view.html new file mode 100644 index 0000000..2eea3e1 --- /dev/null +++ b/front/src/shell/page/view/view.html @@ -0,0 +1,70 @@ + + + + + + \ No newline at end of file diff --git a/front/src/shell/page/view/viewMixin.html b/front/src/shell/page/view/viewMixin.html new file mode 100644 index 0000000..ef3edba --- /dev/null +++ b/front/src/shell/page/view/viewMixin.html @@ -0,0 +1,42 @@ + + \ No newline at end of file diff --git a/front/src/shell/pages/organisation/organisation.html b/front/src/shell/pages/organisation/organisation.html deleted file mode 100644 index e0aeea0..0000000 --- a/front/src/shell/pages/organisation/organisation.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - diff --git a/front/src/shell/pages/organisation/organisation.js b/front/src/shell/pages/organisation/organisation.js deleted file mode 100644 index a20220c..0000000 --- a/front/src/shell/pages/organisation/organisation.js +++ /dev/null @@ -1,24 +0,0 @@ -class ShellOrganisation extends mix(Polymer.Element).with(GlobalConst, Http) { - static get is() { - return 's-organisation'; - } - - constructor(props) { - super(props); - this.views = []; - this.artifacts = []; - } - - connectedCallback() { - super.connectedCallback(); - - if (this.id) { - this.fetchReactivity('GET', `/subscribe/${this.id}`).then((data) => { - const groups = _.groupBy(data, 'event'); - this.setProperties({views: groups['READ_VIEW'], artifacts: groups['READ_ARTIFACT']}); - }); - } - } -} -// Register custom element definition using standard platform API -customElements.define(ShellOrganisation.is, ShellOrganisation); diff --git a/front/src/shell/pages/organisations/organisations.html b/front/src/shell/pages/organisations/organisations.html deleted file mode 100644 index 3cdd13d..0000000 --- a/front/src/shell/pages/organisations/organisations.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - diff --git a/front/src/shell/pages/organisations/organisations.js b/front/src/shell/pages/organisations/organisations.js deleted file mode 100644 index 1cf151e..0000000 --- a/front/src/shell/pages/organisations/organisations.js +++ /dev/null @@ -1,20 +0,0 @@ -class ShellOrganisations extends mix(Polymer.Element).with(GlobalConst, Http) { - static get is() { - return 's-organisations'; - } - - constructor() { - super(); - this.organisations = []; - } - - connectedCallback() { - super.connectedCallback(); - - this.fetchReactivity('GET', `/load/organizations`).then((data) => { - this.setProperties({organisations: data}); - }); - } -} -// Register custom element definition using standard platform API -customElements.define(ShellOrganisations.is, ShellOrganisations); diff --git a/front/src/shell/pages/view/view.html b/front/src/shell/pages/view/view.html deleted file mode 100644 index 42b6383..0000000 --- a/front/src/shell/pages/view/view.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - \ No newline at end of file diff --git a/front/src/shell/pages/view/view.js b/front/src/shell/pages/view/view.js deleted file mode 100644 index 858b7d6..0000000 --- a/front/src/shell/pages/view/view.js +++ /dev/null @@ -1,48 +0,0 @@ -class ShellView extends mix(Polymer.Element).with(GlobalConst, Http) { - static get is() { - return 's-view'; - } - - constructor(props) { - super(props); - this.artifactLimit = 50; - //Limit in pixel before fetching others data - this.threshold = 1500; - this.artifacts = []; - //load on init - this.isLoading = true; - this.maxage = -1; - } - - connectedCallback() { - super.connectedCallback(); - if (this.id) { - this.$.content.addEventListener('scroll', this._handleScroll.bind(this)); - this.fetchNextData(this.artifactLimit, -1); - } - } - - _handleScroll() { - if (this.$.content.scrollTop >= (this.$.content.scrollHeight - this.$.content.offsetHeight) - this.threshold) { - if (!this.isLoading && this.maxage) { - this.setProperties({isLoading: true}); - this.fetchNextData(this.artifactLimit, this.maxage); - } - } - } - - fetchNextData(limit, maxage) { - this.fetchReactivity('GET', `/load/artifacts/${this.id}/limit/${limit}/maxage/${maxage}`) - .then((data) => { - if (data.length) { - this.maxage = data[data.length - 1].updated - 1; - this.setProperties({artifacts: this.artifacts.concat(data), isLoading: false}); - } else if (data.length == 0) { - this.maxage = 0; - this.setProperties({isLoading: false}); - } - }); - } -} -// Register custom element definition using standard platform API -customElements.define(ShellView.is, ShellView); diff --git a/front/src/shell/reducer/artifactReducer.html b/front/src/shell/reducer/artifactReducer.html new file mode 100644 index 0000000..830ec58 --- /dev/null +++ b/front/src/shell/reducer/artifactReducer.html @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/front/src/shell/reducer/organisationsOverviewReducer.html b/front/src/shell/reducer/organisationsOverviewReducer.html new file mode 100644 index 0000000..7332e38 --- /dev/null +++ b/front/src/shell/reducer/organisationsOverviewReducer.html @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/front/src/shell/reducer/viewReducer.html b/front/src/shell/reducer/viewReducer.html new file mode 100644 index 0000000..3dd7b77 --- /dev/null +++ b/front/src/shell/reducer/viewReducer.html @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/front/src/shell/store/redux-mixin.html b/front/src/shell/store/redux-mixin.html new file mode 100644 index 0000000..46d38a8 --- /dev/null +++ b/front/src/shell/store/redux-mixin.html @@ -0,0 +1,25 @@ + + + + + + From 26340f7fa698e4fb3388148e2eb8fe2ae302c4f3 Mon Sep 17 00:00:00 2001 From: nathandm Date: Wed, 5 Jul 2017 19:19:20 +0200 Subject: [PATCH 126/132] fix(bower): update polymer-redux version --- front/bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/bower.json b/front/bower.json index 9130d7e..275e783 100644 --- a/front/bower.json +++ b/front/bower.json @@ -17,7 +17,7 @@ "app-router": "blasten/app-router#master", "lodash": "^4.17.4", "q": "^1.4.1", - "polymer-redux": "polymer-2" + "polymer-redux": "^1.0.1" }, "devDependencies": { "web-component-tester": "^4.0.0", From db0e46a94e7eb2175a2a2c56a6d81db3cccc7d28 Mon Sep 17 00:00:00 2001 From: gdrouet Date: Thu, 6 Jul 2017 14:58:59 +0200 Subject: [PATCH 127/132] bower: fix lodash resolution --- front/bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/bower.json b/front/bower.json index 275e783..0c41bf1 100644 --- a/front/bower.json +++ b/front/bower.json @@ -24,7 +24,7 @@ "sw-toolbox": "3.4.0" }, "resolutions": { - "lodash": "4.0.0", + "lodash": "4.17.4", "webcomponentsjs": "v1.0.0-rc.6" } } From ee09d4d7aecaa58c3e0041cce84480de16e29f16 Mon Sep 17 00:00:00 2001 From: nathandm Date: Sun, 20 Aug 2017 14:41:03 +0200 Subject: [PATCH 128/132] fix(version): fix bower version remove rc --- front/bower.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/front/bower.json b/front/bower.json index 0c41bf1..aa6cf1a 100644 --- a/front/bower.json +++ b/front/bower.json @@ -4,27 +4,27 @@ "description": "Web application to manage the activity that matters", "author": "Nathan DAMIE", "dependencies": { - "polymer": "2.0.0-rc.5", - "paper-card": "PolymerElements/paper-card#2.0-preview", - "app-layout": "PolymerElements/app-layout#2.0-preview", - "iron-icons": "PolymerElements/iron-icons#2.0-preview", - "iron-icon": "PolymerElements/iron-icon#2.0-preview", - "iron-flex-layout": "2.0-preview", - "iron-collapse": "2.0-preview", - "paper-button": "2.0-preview", - "paper-checkbox": "2.0-preview", - "paper-icon-button": "2.0-preview", "app-router": "blasten/app-router#master", - "lodash": "^4.17.4", + "lodash": "4.17.4", "q": "^1.4.1", - "polymer-redux": "^1.0.1" + "polymer-redux": "^1.0.1", + "polymer": "Polymer/polymer#2.0.2", + "paper-card": "PolymerElements/paper-card#2.0.0", + "app-layout": "PolymerElements/app-layout#2.0.1", + "iron-icons": "PolymerElements/iron-icons#2.0.1", + "iron-icon": "PolymerElements/iron-icon#2.0.1", + "iron-flex-layout": "2.0.0", + "iron-collapse": "2.0.0", + "paper-button": "2.0.0", + "paper-checkbox": "2.0.1", + "paper-icon-button": "2.0.0" }, "devDependencies": { - "web-component-tester": "^4.0.0", - "sw-toolbox": "3.4.0" + "web-component-tester": "6.0.1", + "sw-toolbox": "3.6.0" }, "resolutions": { "lodash": "4.17.4", - "webcomponentsjs": "v1.0.0-rc.6" + "webcomponentsjs": "1.0.2" } } From a829dd731b1fa28a33b6830f665eaebebbe6c0f3 Mon Sep 17 00:00:00 2001 From: nathandm Date: Sun, 20 Aug 2017 15:58:20 +0200 Subject: [PATCH 129/132] style(front): change checkstyle + node 8.4 --- .travis.yml | 3 +- front/.nvmrc | 1 + front/package.json | 118 +- front/src/component/app-card.html | 2778 +++++++++-------- front/src/component/app-orga.html | 180 +- front/src/component/app-view.html | 120 +- front/src/component/color-konami.html | 50 +- front/src/service/apiDomain.service.js | 2 +- front/src/service/const.service.js | 4 +- front/src/service/http.service.js | 4 +- front/src/shell/navbar/navbar.html | 144 +- front/src/shell/page/404/view-404.html | 146 +- .../shell/page/organisation/organisation.html | 86 +- .../page/organisation/organisationsMixin.html | 68 +- .../page/organisations/organisations.html | 122 +- .../organisationsOverviewMixin.html | 56 +- front/src/shell/page/view/view.html | 122 +- front/src/shell/page/view/viewMixin.html | 70 +- front/src/shell/reducer/artifactReducer.html | 38 +- .../reducer/organisationsOverviewReducer.html | 19 +- front/src/shell/reducer/viewReducer.html | 30 +- front/src/shell/store/redux-mixin.html | 32 +- script/front-end.sh | 4 +- 23 files changed, 2116 insertions(+), 2081 deletions(-) create mode 100644 front/.nvmrc diff --git a/.travis.yml b/.travis.yml index 2e9c021..320aa4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ env: - MODULE=core/java-lib - MODULE=core/broadcaster global: - - NODEJS_VERSION: 6.10.0 - ENCRYPTION_LABEL: "094e2d6354aa" - COMMIT_AUTHOR_EMAIL: "guidrouet@gmail.com" - secure: jyieF62Bdxn8KzePW73qAWOsV6n83gIGDpKGpgbpbCDG+R5AcMqFSXf32A7So148YczZIjlhWUx85Vrol9NURpimxcFfZU9nKCPjZt7QpGuzXInbxy8Qrze1I/PN40nMVGxNEam7pwXIMZwbOFPv/PG+fDw7BCDSfsoTwTMbk1FvjhpBngo6DBtbyld30HEhIVSSLhROgcQ7zzcfHYdu9IxvnDFnuNict1m1+HGJf0jDAbFRzGteMq7/tu+rM9HHd5UyCSI2sGWo4P3tAcXw/l3VV/MxhG6cRofvQQEY7H+2JpJ5U6CDprrN7sOUrc777cmOzXzB1sYLaNNniQiScQtZJTYcJbhPF/rOYn8FjrFDMxC0EB7oQQ58Z4dUViKNAdR54zxFwm/OqDssAV43fAOFfkiZGfdKjR+CQQMWRD7UdaCfsy3w/xFw5KiNObyUtvbBJABVYWjsIIF0yU20Qu8Cr5DCfUjUTyNiWz/LjN3cQjtv6HB7A3UWc/ohjhU/GZoWvvJ5WySKLMuL8msH5/R3UE+u8TZYTi9pSG/k6BSoVO4DRAmvG/7FdepwHJYnW1Ol1Bd2u0OdqiwhByu0yDH9jolJ/VYBq8A880nU+dW5SVhwTNSvLgo3jclr4IwkCydZvZ6YjiqpKSqW69TRWyPLOFVLtn943yFX89OXhwY= @@ -41,4 +40,4 @@ deploy: branch: travis-deploy condition: $MODULE = 'core/broadcaster' notifications: - slack: reactivity:eHHORgWgihkLbNPLR4B7RAOv \ No newline at end of file + slack: reactivity:eHHORgWgihkLbNPLR4B7RAOv diff --git a/front/.nvmrc b/front/.nvmrc new file mode 100644 index 0000000..a2f28f4 --- /dev/null +++ b/front/.nvmrc @@ -0,0 +1 @@ +8.4.0 diff --git a/front/package.json b/front/package.json index 2c04125..3b05358 100644 --- a/front/package.json +++ b/front/package.json @@ -1,59 +1,63 @@ { - "name": "web-front", - "version": "0.0.1", - "description": "Web application to manage the activity that matters", - "keywords": [ - "e2e", - "End-To-End-Test", - "protractor", - "selenium", - "jasmine", - "saucelabs", - "polymer" - ], - "license": "MIT", - "contributors": [ - "Quentin Levaslot " - ], - "repository": { - "type": "git", - "url": "https://github.com/reactivity-io/reactivity.git" - }, - "bugs": { - "url": "https://github.com/reactivity-io/reactivity/issues" - }, - "homepage": "https://github.com/reactivity-io/reactivity/blob/front/front/README.md", - "scripts": { - "postinstall": "bower install", - "start": "gulp", - "dist": "gulp dist", - "test": "./node_modules/.bin/wct --skip-plugin sauce --plugin local", - "webdriver:update": "webdriver-manager update", - "webdriver:start": "webdriver-manager start --standalone", - "e2e": "./node_modules/.bin/protractor e2e/conf.js", - "lint": "./node_modules/.bin/eslint ." - }, - "author": "Nathan damie ", - "devDependencies": { - "bower": "^1.8.0", - "browser-sync": "^2.18.5", - "connect-history-api-fallback": "^1.3.0", - "del": "^2.2.2", - "eslint": "^3.13.1", - "eslint-config-google": "^0.7.1", - "gulp": "github:gulpjs/gulp#4.0", - "gulp-file": "^0.3.0", - "http-proxy-middleware": "^0.17.3", - "js-yaml": "^3.7.0", - "merge-stream": "^1.0.1", - "polymer-build": "^0.8.1", - "protractor": "^5.0.0", - "web-component-tester": "^5.0.0", - "webdriver-manager": "^12.0.2", - "through2": "^2.0.3" - }, - "dependencies": { - "redux": "^3.6.0", - "redux-thunk": "^2.2.0" - } + "name": "web-front", + "version": "0.0.1", + "description": "Web application to manage the activity that matters", + "keywords": [ + "e2e", + "End-To-End-Test", + "protractor", + "selenium", + "jasmine", + "saucelabs", + "polymer" + ], + "license": "MIT", + "contributors": [ + "Quentin Levaslot " + ], + "repository": { + "type": "git", + "url": "https://github.com/reactivity-io/reactivity.git" + }, + "bugs": { + "url": "https://github.com/reactivity-io/reactivity/issues" + }, + "homepage": "https://github.com/reactivity-io/reactivity/blob/front/front/README.md", + "scripts": { + "postinstall": "bower install", + "start": "gulp", + "dist": "gulp dist", + "test": "./node_modules/.bin/wct --skip-plugin sauce --plugin local", + "webdriver:update": "webdriver-manager update", + "webdriver:start": "webdriver-manager start --standalone", + "e2e": "./node_modules/.bin/protractor e2e/conf.js", + "lint": "./node_modules/.bin/eslint ." + }, + "author": "Nathan damie ", + "devDependencies": { + "bower": "^1.8.0", + "browser-sync": "^2.18.5", + "connect-history-api-fallback": "^1.3.0", + "del": "^2.2.2", + "eslint": "^3.13.1", + "eslint-config-google": "^0.7.1", + "gulp": "github:gulpjs/gulp#4.0", + "gulp-file": "^0.3.0", + "http-proxy-middleware": "^0.17.3", + "js-yaml": "^3.7.0", + "merge-stream": "^1.0.1", + "polymer-build": "^0.8.1", + "protractor": "^5.0.0", + "web-component-tester": "^5.0.0", + "webdriver-manager": "^12.0.2", + "through2": "^2.0.3" + }, + "dependencies": { + "redux": "^3.6.0", + "redux-thunk": "^2.2.0" + }, + "engines": { + "node": "8.4.0", + "npm": "5.3.4" + } } diff --git a/front/src/component/app-card.html b/front/src/component/app-card.html index 4c944d8..80ebef7 100644 --- a/front/src/component/app-card.html +++ b/front/src/component/app-card.html @@ -1,1362 +1,1376 @@ - - + ready() { + super.ready(); + this.fetchOrganisationsOverview(); + } + } + // Register custom element definition using standard platform API + customElements.define(ShellOrganisations.is, ShellOrganisations); + + diff --git a/front/src/shell/page/organisations/organisationsOverviewMixin.html b/front/src/shell/page/organisations/organisationsOverviewMixin.html index 54b41a2..935ad62 100644 --- a/front/src/shell/page/organisations/organisationsOverviewMixin.html +++ b/front/src/shell/page/organisations/organisationsOverviewMixin.html @@ -1,35 +1,35 @@ \ No newline at end of file + } + }; + diff --git a/front/src/shell/page/view/view.html b/front/src/shell/page/view/view.html index 2eea3e1..476ebb6 100644 --- a/front/src/shell/page/view/view.html +++ b/front/src/shell/page/view/view.html @@ -1,70 +1,70 @@ -