[GitHub] incubator-griffin pull request #456: [GRIFFIN-213] Custom connector support

2018-11-17 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/456#discussion_r234425707
  
--- Diff: 
measure/src/main/scala/org/apache/griffin/measure/datasource/connector/DataConnectorFactory.scala
 ---
@@ -84,6 +87,26 @@ object DataConnectorFactory extends Loggable {
 }
   }
 
+  private def getCustomConnector(session: SparkSession,
+ context: StreamingContext,
+ param: DataConnectorParam,
+ storage: TimestampStorage,
+ maybeClient: 
Option[StreamingCacheClient]): DataConnector = {
+val className = param.getConfig("class").asInstanceOf[String]
+val cls = Class.forName(className)
+if (classOf[BatchDataConnector].isAssignableFrom(cls)) {
+  val ctx = BatchDataConnectorContext(session, param, storage)
+  val meth = cls.getDeclaredMethod("apply", 
classOf[BatchDataConnectorContext])
+  meth.invoke(null, ctx).asInstanceOf[BatchDataConnector]
+} else if (classOf[StreamingDataConnector].isAssignableFrom(cls)) {
+  val ctx = StreamingDataConnectorContext(session, context, param, 
storage, maybeClient)
+  val meth = cls.getDeclaredMethod("apply", 
classOf[StreamingDataConnectorContext])
+  meth.invoke(null, ctx).asInstanceOf[StreamingDataConnector]
+} else {
+  throw new ClassCastException("")
--- End diff --

It would be nice to have here message that custom connector class must 
extend `BatchDataConnector` or `StreamingDataConnector`


---


[GitHub] incubator-griffin issue #444: Define griffin plain-vanilla hook.

2018-11-08 Thread gavlyukovskiy
Github user gavlyukovskiy commented on the issue:

https://github.com/apache/incubator-griffin/pull/444
  
Yes, I showed this in my example - take all hooks from the context, but use 
only those that configured in property.


---


[GitHub] incubator-griffin issue #444: Define griffin plain-vanilla hook.

2018-11-08 Thread gavlyukovskiy
Github user gavlyukovskiy commented on the issue:

https://github.com/apache/incubator-griffin/pull/444
  
> Can we provide several individually implements in configuration like 
GriffinHook1, GriffionHook2, and autowired all individual hooks instances into 
List?
You can define all hooks in the context, but use only ones that explicitly 
set in property

> Do we really need that much hooks?
We are deploying griffin with custom set of plugins bundled in jar, we will 
probably define few custom hooks in that jar so that they will be available in 
griffin context


---


[GitHub] incubator-griffin pull request #444: Define griffin plain-vanilla hook.

2018-11-08 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/444#discussion_r231981734
  
--- Diff: 
service/src/main/java/org/apache/griffin/core/event/GriffinEventListeners.java 
---
@@ -0,0 +1,50 @@
+package org.apache.griffin.core.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Configuration
+public class GriffinEventListeners {
+private static final Logger LOGGER = LoggerFactory
+.getLogger(GriffinEventListeners.class);
+
+@Autowired
+private ApplicationContext context;
+@Value("#{'${internal.event.listeners}'.split(',')}")
+private List listeners;
+
+@Bean
+public List getListenersRegistered() {
--- End diff --

I see that you getting beans by names from the context, am I right that 
you're trying to declare only beans that user has specified in the property? I 
think it will not work, because `@Autowired List eventListeners` 
will always inject all beans of given type from the context.
For example if you do:
```
@Bean
public static List list() {
return Arrays.asList(
new GriffinHook("1"),
new GriffinHook("2")
);
}

@Bean
public static GriffinHook hook3() {
return new GriffinHook("3");
}

@Autowired
private final List griffinHooks;
```
in this case spring will try to inject list of beans of given type - list 
containing only `hook3`.

Instead I would propose to remove this bean declaration and make changes in 
`GriffinEventManager` to get all beans from context and use only beans 
specified in the config:
```
public class GriffinEventManager {

@Autowired
private ApplicationContext applicationContext;
@Value("#{'${internal.event.listeners}'.split(',')}")
private Set enabledListeners;

private List eventListeners;

@PostConstruct
void initializeListeners() {
List eventListeners = new ArrayList<>();
applicationContext.getBeansOfType(GriffinHook.class)
.forEach((beanName, listener) -> {
if (enabledListeners.contains(beanName)) {
eventListeners.add(listener);
}
});
this.eventListeners = eventListeners;
}

...
}
```


---


[GitHub] incubator-griffin issue #444: Define griffin plain-vanilla hook.

2018-11-08 Thread gavlyukovskiy
Github user gavlyukovskiy commented on the issue:

https://github.com/apache/incubator-griffin/pull/444
  
Left a comment regarding injection. Tests didn't catch it because you 
explicitly specified all hooks that are registered in the context, I would add 
one more and leave it unused just for testing purpose.


---


[GitHub] incubator-griffin pull request #444: Define griffin plain-vanilla hook.

2018-11-05 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/444#discussion_r231008571
  
--- Diff: 
service/src/main/java/org/apache/griffin/core/event/GriffinEventListeners.java 
---
@@ -0,0 +1,51 @@
+package org.apache.griffin.core.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Configuration
+@ConfigurationProperties(prefix = "internal.event")
--- End diff --

I'm not sure you need it here, this will map all class fields (that have 
setters and getters) to properties.


---


[GitHub] incubator-griffin pull request #446: [GRIFFIN-203] "Plaintext mode" for meas...

2018-10-24 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/446#discussion_r228039612
  
--- Diff: ui/angular/src/app/measure/create-measure/raw/raw.component.ts ---
@@ -0,0 +1,146 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+import {Component, OnInit} from "@angular/core";
+import {ServiceService} from "../../../service/service.service";
+import {TREE_ACTIONS, ITreeOptions} from "angular-tree-component";
+import {ToasterService} from "angular2-toaster";
+import * as $ from "jquery";
+import {HttpClient} from "@angular/common/http";
+import {ActivatedRoute, Router} from "@angular/router";
+import {AfterViewChecked, ElementRef} from "@angular/core";
+import {MeasureFormatService, Format} from 
"../../../service/measure-format.service";
+
+@Component({
+  selector: "app-raw",
+  templateUrl: "./raw.component.html",
+  providers: [ServiceService, MeasureFormatService],
+  styleUrls: ["./raw.component.css"]
+})
+export class RawComponent implements AfterViewChecked {
+
+  constructor(
+private elementRef: ElementRef,
+private toasterService: ToasterService,
+private measureFormatService: MeasureFormatService,
+private http: HttpClient,
+private router: Router,
+public serviceService: ServiceService
+  ) {
+  }
+
+  data = "";
+  valid = false;
+  Format: typeof Format = Format;
+  format: Format;
+  createResult: any;
+  public visible = false;
+  public visibleAnimate = false;
+
+  public hide(): void {
+this.visibleAnimate = false;
+setTimeout(() => (this.visible = false), 300);
+$("#save").removeAttr("disabled");
+  }
+
+  public onContainerClicked(event: MouseEvent): void {
+if ((event.target).classList.contains("modal")) {
+  this.hide();
+}
+  }
+
+  onResize(event) {
+this.resizeWindow();
+  }
+
+  resizeWindow() {
+var stepSelection = ".formStep";
+$(stepSelection).css({
+  height: window.innerHeight - $(stepSelection).offset().top
+});
+$("fieldset").height(
+  $(stepSelection).height() -
+  $(stepSelection + ">.stepDesc").height() -
+  $(".btn-container").height() -
+  130
+);
+$(".y-scrollable").css({
+  height: $("fieldset").height()
+});
+  }
+
+  submit(form) {
+if (!form.valid) {
+  this.toasterService.pop(
+"error",
+"Error!",
+"please complete the form in this step before proceeding"
+  );
+  return false;
+}
+this.visible = true;
+setTimeout(() => (this.visibleAnimate = true), 100);
+  }
+
+  save() {
+let measure2Save = this.measureFormatService.parse(this.data, 
this.format);
+console.log(measure2Save);
+let addModels = this.serviceService.config.uri.addModels;
+$("#save").attr("disabled", "true");
+this.http.post(addModels, measure2Save).subscribe(
+  data => {
+this.createResult = data;
+this.hide();
+this.router.navigate(["/measures"]);
+  },
+  err => {
+let response = JSON.parse(err.error);
+if (response.code === '40901') {
+  this.toasterService.pop("error", "Error!", "Measure name already 
exists!");
+} else {
+  this.toasterService.pop("error", "Error!", response.message);
+}
+console.log("Error when creating measure");
+  }
+);
+  }
+
+  onInputChange() {
--- End diff --

As I can see it's done on every change, but is not noticeable on my laptop. 
Not sure if it will affect others, but anyway it's much easier to edit measure 
in normal editor and just copy-past it here :) 


---


[GitHub] incubator-griffin pull request #446: [GRIFFIN-203] "Plaintext mode" for meas...

2018-10-24 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/446#discussion_r228037408
  
--- Diff: ui/angular/src/app/measure/create-measure/raw/raw.component.ts ---
@@ -0,0 +1,180 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+import {Component, OnInit} from "@angular/core";
+import {ServiceService} from "../../../service/service.service";
+import {TREE_ACTIONS, ITreeOptions} from "angular-tree-component";
+import {ToasterService} from "angular2-toaster";
+import * as $ from "jquery";
+import {HttpClient} from "@angular/common/http";
+import {ActivatedRoute, Router} from "@angular/router";
+import {AfterViewChecked, ElementRef} from "@angular/core";
+import {MeasureFormatService, Format} from 
"../../../service/measure-format.service";
+
+@Component({
+  selector: "app-raw",
+  templateUrl: "./raw.component.html",
+  providers: [ServiceService, MeasureFormatService],
+  styleUrls: ["./raw.component.css"]
+})
+export class RawComponent implements AfterViewChecked, OnInit {
+
+  constructor(
+private elementRef: ElementRef,
+private toasterService: ToasterService,
+private measureFormatService: MeasureFormatService,
+private http: HttpClient,
+private router: Router,
+public serviceService: ServiceService
+  ) {
+  }
+
+  data = "";
+  valid = false;
+  Format: typeof Format = Format;
+  format: Format;
+  createResult: any;
+  public visible = false;
+  public visibleAnimate = false;
+
+  public hide(): void {
+this.visibleAnimate = false;
+setTimeout(() => (this.visible = false), 300);
+$("#save").removeAttr("disabled");
+  }
+
+  public onContainerClicked(event: MouseEvent): void {
+if ((event.target).classList.contains("modal")) {
+  this.hide();
+}
+  }
+
+  onResize(event) {
+this.resizeWindow();
+  }
+
+  resizeWindow() {
+var stepSelection = ".formStep";
+$(stepSelection).css({
+  height: window.innerHeight - $(stepSelection).offset().top
+});
+$("fieldset").height(
+  $(stepSelection).height() -
+  $(stepSelection + ">.stepDesc").height() -
+  $(".btn-container").height() -
+  130
+);
+$(".y-scrollable").css({
+  height: $("fieldset").height()
+});
+  }
+
+  submit(form) {
+if (!form.valid) {
+  this.toasterService.pop(
+"error",
+"Error!",
+"please complete the form in this step before proceeding"
+  );
+  return false;
+}
+this.visible = true;
+setTimeout(() => (this.visibleAnimate = true), 100);
+  }
+
+  save() {
+let measure2Save = this.measureFormatService.parse(this.data, 
this.format);
+console.log(measure2Save);
+let addModels = this.serviceService.config.uri.addModels;
+$("#save").attr("disabled", "true");
+this.http.post(addModels, measure2Save).subscribe(
+  data => {
+this.createResult = data;
+this.hide();
+this.router.navigate(["/measures"]);
+  },
+  err => {
+let response = JSON.parse(err.error);
+if (response.code === '40901') {
+  this.toasterService.pop("error", "Error!", "Measure name already 
exists!");
+} else {
+  this.toasterService.pop("error", "Error!", "Measure is not 
valid");
--- End diff --

fixed


---


[GitHub] incubator-griffin pull request #446: [GRIFFIN-203] "Plaintext mode" for meas...

2018-10-24 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/446#discussion_r228037399
  
--- Diff: ui/angular/src/app/measure/create-measure/raw/raw.component.ts ---
@@ -0,0 +1,180 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+import {Component, OnInit} from "@angular/core";
+import {ServiceService} from "../../../service/service.service";
+import {TREE_ACTIONS, ITreeOptions} from "angular-tree-component";
+import {ToasterService} from "angular2-toaster";
+import * as $ from "jquery";
+import {HttpClient} from "@angular/common/http";
+import {ActivatedRoute, Router} from "@angular/router";
+import {AfterViewChecked, ElementRef} from "@angular/core";
+import {MeasureFormatService, Format} from 
"../../../service/measure-format.service";
+
+@Component({
+  selector: "app-raw",
+  templateUrl: "./raw.component.html",
+  providers: [ServiceService, MeasureFormatService],
+  styleUrls: ["./raw.component.css"]
+})
+export class RawComponent implements AfterViewChecked, OnInit {
+
+  constructor(
+private elementRef: ElementRef,
+private toasterService: ToasterService,
+private measureFormatService: MeasureFormatService,
+private http: HttpClient,
+private router: Router,
+public serviceService: ServiceService
+  ) {
+  }
+
+  data = "";
+  valid = false;
+  Format: typeof Format = Format;
+  format: Format;
+  createResult: any;
+  public visible = false;
+  public visibleAnimate = false;
+
+  public hide(): void {
+this.visibleAnimate = false;
+setTimeout(() => (this.visible = false), 300);
+$("#save").removeAttr("disabled");
+  }
+
+  public onContainerClicked(event: MouseEvent): void {
+if ((event.target).classList.contains("modal")) {
+  this.hide();
+}
+  }
+
+  onResize(event) {
+this.resizeWindow();
+  }
+
+  resizeWindow() {
+var stepSelection = ".formStep";
+$(stepSelection).css({
+  height: window.innerHeight - $(stepSelection).offset().top
+});
+$("fieldset").height(
+  $(stepSelection).height() -
+  $(stepSelection + ">.stepDesc").height() -
+  $(".btn-container").height() -
+  130
+);
+$(".y-scrollable").css({
+  height: $("fieldset").height()
+});
+  }
+
+  submit(form) {
+if (!form.valid) {
+  this.toasterService.pop(
+"error",
+"Error!",
+"please complete the form in this step before proceeding"
+  );
+  return false;
+}
+this.visible = true;
+setTimeout(() => (this.visibleAnimate = true), 100);
+  }
+
+  save() {
+let measure2Save = this.measureFormatService.parse(this.data, 
this.format);
+console.log(measure2Save);
+let addModels = this.serviceService.config.uri.addModels;
+$("#save").attr("disabled", "true");
+this.http.post(addModels, measure2Save).subscribe(
+  data => {
+this.createResult = data;
+this.hide();
+this.router.navigate(["/measures"]);
+  },
+  err => {
+let response = JSON.parse(err.error);
+if (response.code === '40901') {
+  this.toasterService.pop("error", "Error!", "Measure name already 
exists!");
+} else {
+  this.toasterService.pop("error", "Error!", "Measure is not 
valid");
+}
+console.log("Error when creating measure");
+  }
+);
+  }

[GitHub] incubator-griffin pull request #446: [GRIFFIN-203] "Plaintext mode" for meas...

2018-10-24 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/446#discussion_r228036531
  
--- Diff: ui/angular/src/app/measure/create-measure/raw/raw.component.html 
---
@@ -0,0 +1,100 @@
+
+
+  
+Create Measure
+  
+  
+
+  
+
+  
+
+  
+1
+  
+   Configuration 
+
+  
+
+  
+  
+Please setup the measure required 
information
+
+  
+
+  
+Required Information
+  
+  
+
+  
+
+  Raw measure:
+
+
+  
+JSON
+YAML
+  
+
+  
+  
+
+Measure is not valid
+  
+
+  
+  
+
+   After submitted, 
please go to "Measures"
+  to check the measure status
+
+  
+
+  
+  
+
+
+  Submit
+
+  
+
+  
+  
+
--- End diff --

I just didn't captured whole window, I've just checked and it's centered.


---


[GitHub] incubator-griffin pull request #446: [GRIFFIN-203] "Plaintext mode" for meas...

2018-10-24 Thread gavlyukovskiy
GitHub user gavlyukovskiy opened a pull request:

https://github.com/apache/incubator-griffin/pull/446

[GRIFFIN-203] "Plaintext mode" for measure creation

Created json/yaml measure creation flow:
![2018-10-24 21 41 
44](https://user-images.githubusercontent.com/15277543/47476643-4aa37680-d7d6-11e8-9488-fe2e247617a1.gif)

Added ability to view measure as yaml:
![2018-10-24 21 42 
23](https://user-images.githubusercontent.com/15277543/47476652-4d9e6700-d7d6-11e8-86f3-f35fdc44dda1.gif)

You can merge this pull request into a Git repository by running:

$ git pull https://github.com/gavlyukovskiy/incubator-griffin raw-measures

Alternatively you can review and apply these changes as the patch at:

https://github.com/apache/incubator-griffin/pull/446.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

This closes #446


commit e07a7e169b18f67fd5661ab085524c3bf9206c8b
Author: Arthur Gavlyukovskiy 
Date:   2018-10-25T04:53:01Z

[GRIFFIN-203] "Plaintext mode" for measure creation
Created json/yaml measure creation flow
Added ability to view measure as yaml




---


[GitHub] incubator-griffin pull request #435: [GRIFFIN-206] fix job timezone when cre...

2018-10-11 Thread gavlyukovskiy
Github user gavlyukovskiy commented on a diff in the pull request:

https://github.com/apache/incubator-griffin/pull/435#discussion_r224560857
  
--- Diff: 
service/src/test/java/org/apache/griffin/core/util/TimeUtilTest.java ---
@@ -110,4 +111,36 @@ public void testFormatWithIllegalException() {
 TimeUtil.format(format, time, TimeZone.getTimeZone(timeZone));
 }
 
+@Test
+public void testGetTimeZone() {
+HashMap tests = new HashMap<>();
+tests.put("", TimeZone.getDefault().getID());
+// standard cases
+tests.put("GMT", "GMT");
+tests.put("GMT+1", "GMT+01:00");
+tests.put("GMT+1:00", "GMT+01:00");
+tests.put("GMT+01:00", "GMT+01:00");
+tests.put("GMT-1", "GMT-01:00");
+tests.put("GMT-1:00", "GMT-01:00");
+tests.put("GMT-01:00", "GMT-01:00");
+// values pushed by UI for jobs
+tests.put("GMT1", "GMT");
+tests.put("GMT1:00", "GMT");
+tests.put("GMT01:00", "GMT");
+// values generated by UI for datasets in a past
+tests.put("UTC1", "GMT");
+tests.put("UTC1:00", "GMT");
+tests.put("UTC01:00", "GMT");
+tests.put("UTC-1", "GMT");
+tests.put("UTC-1:00", "GMT");
+tests.put("UTC-01:00", "GMT");
+for (HashMap.Entry e: tests.entrySet()) {
--- End diff --

```
tests.forEach((key, value) -> {...})
```
Looks even better :)


---