Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions chronicle/raconteur/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM ubuntu:16.04

RUN apt-get update && apt-get install -y ca-certificates \
&& apt-get clean

RUN mkdir -p /raconteur
WORKDIR /raconteur
COPY raconteur /raconteur

CMD ["/raconteur/raconteur"]
29 changes: 29 additions & 0 deletions chronicle/raconteur/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
include ../../makelib
header = $(call baseheader, $(1), raconteur)

DOCKER_REPO ?= $(DOCKER_STAGE_REPO)
DOCKER_IMAGE ?= raconteur
DOCKER_TAG ?= master

build:
$(call header, Building)
GOOS=linux CGO_ENABLED=0 go build -o raconteur ./*.go

test:
$(call header, Testing)
true

docker:
$(call header, Dockerizing)
docker build -t $(DOCKER_IMAGE) .

docker-push:
$(call header, Registering)
docker tag $(DOCKER_IMAGE) $(DOCKER_REPO)/$(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_REPO)/$(DOCKER_IMAGE):$(DOCKER_TAG)

clean:
$(call header, Cleaning)
rm -rf ./pkg raconteur

.PHONY: build test docker docker-push
4 changes: 4 additions & 0 deletions chronicle/raconteur/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# raconteur

Processes activities, pulls out object activity occurred on and executes javascript
registered for that object/object type and activity type
57 changes: 57 additions & 0 deletions chronicle/raconteur/consumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"encoding/base64"
"errors"
"log"
"os/exec"

"github.com/FoxComm/highlander/middlewarehouse/models/activities"
"github.com/FoxComm/metamorphosis"
)

type EventConsumer struct {
ApiUrl string
}

func NewEventConsumer(apiUrl string) (*EventConsumer, error) {
if apiUrl == "" {
return nil, errors.New("API_URL is required")
}

return &EventConsumer{apiUrl}, nil
}

func (c EventConsumer) processActivity(activity activities.ISiteActivity) error {
log.Printf("Processing %s: %s", activity.Type(), activity.Data())

scriptText := "console.log('ACTIVITY: ' + type); api.get('/v1/public/products/' + data.cart.lineItems.skus[0].productFormId).then(function (resp) { console.log('RESPONSE: ');console.log(resp);});"

data := base64.StdEncoding.EncodeToString([]byte(activity.Data()))
script := base64.StdEncoding.EncodeToString([]byte(scriptText))

//Get from DB
jwt := "test-jwt"
stripeKey := "test-stripe"

out, err := exec.Command("node", "--harmony", "executor/main.js", c.ApiUrl, stripeKey, jwt, activity.Type(), data, script).CombinedOutput()

log.Printf("OUT: %s\n", out)
log.Printf("ERR: %v\n", err)
return nil
}

func (c EventConsumer) Handler(message metamorphosis.AvroMessage) error {
activity, err := activities.NewActivityFromAvro(message)
if err != nil {
log.Fatalf("Unable to read activity with error %s", err.Error())
return err
}

log.Printf("Activity: %s", activity.Type())
err = c.processActivity(activity)
if err != nil {
log.Fatalf("Unable to parse json with error %s", err.Error())
}
return err
}
61 changes: 61 additions & 0 deletions chronicle/raconteur/executor/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const _ = require('lodash');
const vm = require('vm');
const fox = require('api-js');
const superagent = require('superagent');


function createApi(apiBaseUrl ,stripeKey, options = {}) {

if (!apiBaseUrl) {
throw new Error('API_URL is not defined in process.env');
}

let agent = superagent;

if (options.preserveCookies) {
agent = superagent.agent();
}

return new fox.default({
api_url: apiBaseUrl + '/api',
stripe_key: stripeKey,
agent,
handleResponse: options.handleResponse,
});
}

//Parse args
let apiUrl = process.argv[2];
let stripeKey = process.argv[3];
let jwt = process.argv[4];
let type = process.argv[5];
let dataBase64 = process.argv[6];
let scriptBase64 = process.argv[7];

//decode arguments
let data = new Buffer(dataBase64, 'base64').toString('ascii')
let jsonData = JSON.parse(data);

let scriptText = new Buffer(scriptBase64, 'base64').toString('ascii')

//setup execution context
let api = createApi(apiUrl, stripeKey);

console.log("ACTIVITY: " + type);
console.log("DATA: " + data);
console.log("SCRIPT: " + scriptText);

const sandbox = {
console: console,
api: api,
type: type,
data: jsonData
};

//run script
const script = new vm.Script(scriptText);
script.runInNewContext(sandbox, {displayErrors: true});




35 changes: 35 additions & 0 deletions chronicle/raconteur/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"log"
"os"

"github.com/FoxComm/highlander/middlewarehouse/consumers"
"github.com/FoxComm/metamorphosis"
)

const (
groupId = "raconteur-1"
clientId = "raconteur"
)

var apiUrl = os.Getenv("API_URL")

func main() {
config, err := consumers.MakeConsumerConfig()
if err != nil {
log.Fatalf("Unable to initialize consumer with error %s", err.Error())
}

consumer, err := metamorphosis.NewConsumer(config.ZookeeperURL, config.SchemaRepositoryURL, metamorphosis.OffsetResetLargest)
if err != nil {
log.Fatalf("Unable to connect to Kafka with error %s", err.Error())
}

consumer.SetGroupID(groupId)
consumer.SetClientID(clientId)

oc, err := NewEventConsumer(apiUrl)

consumer.RunTopic(config.Topic, oc.Handler)
}
22 changes: 22 additions & 0 deletions chronicle/raconteur/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "executor",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"api-js": "git+ssh://git@github.com/FoxComm/api-js.git",
"crypto-js": "^3.1.9-1",
"faker": "^3.1.0",
"lodash": "^4.17.4",
"nightmare": "2.7.0",
"stripe": "^4.15.0",
"superagent": "^3.4.0",
"system": "^1.2.0"
}
}
5 changes: 5 additions & 0 deletions phoenix-scala/app/failures/ObjectFailures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ object ObjectFailures {
NotFoundFailure404(s"Context with name $name cannot be found")
}

object ObjectNotFoundForContext {
def apply(id: Int, context: String): NotFoundFailure404 =
NotFoundFailure404(s"Object $id not found for context $context")
}

case class ObjectValidationFailure(kind: String, id: Int, errors: String) extends Failure {
override def description = s"Object $kind with id=$id doesn't pass validation: $errors"
}
Expand Down
45 changes: 45 additions & 0 deletions phoenix-scala/app/models/chronicle/ActivityKindHandlers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package models.chronicle

import java.time.Instant

import models.objects.{GenericObjects, GenericObject, ObjectHead, ObjectHeads}
import models.objects.ObjectUtils.InsertResult
import shapeless._
import slick.lifted.Tag
import utils.Validation
import utils.db.ExPostgresDriver.api._
import utils.db._

import com.github.tminglei.slickpg._

case class ActivityKindHandler(id: Int = 0,
scope: LTree,
kind: String,
activityHandlerHead: Int,
createdAt: Instant = Instant.now)
extends FoxModel[ActivityKindHandler]
with Validation[ActivityKindHandler]

class ActivityKindHandlers(tag: Tag) extends FoxTable[ActivityKindHandler](tag, "users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def scope = column[LTree]("scope")
def kind = column[String]("kind")
def activityHandlerHead = column[Int]("activity_handler_head")
def createdAt = column[Instant]("created_at")

def * =
(id, scope, kind, activityHandlerHead, createdAt) <> ((ActivityKindHandler.apply _).tupled, ActivityKindHandler.unapply)

def activityHead =
foreignKey(GenericObjects.tableName, activityHandlerHead, GenericObjects)(_.id)
}

object ActivityKindHandlers
extends FoxTableQuery[ActivityKindHandler, ActivityKindHandlers](new ActivityKindHandlers(_))
with ReturningId[ActivityKindHandler, ActivityKindHandlers] {

val returningLens: Lens[ActivityKindHandler, Int] = lens[ActivityKindHandler].id

def filterByKind(kind: String): QuerySeq =
filter(_.kind === kind)
}
60 changes: 60 additions & 0 deletions phoenix-scala/app/models/objects/GenericObjects.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package models.objects

import java.time.Instant

import shapeless._
import slick.lifted.Tag
import utils.Validation
import utils.db.ExPostgresDriver.api._
import utils.db._

import com.github.tminglei.slickpg._

case class GenericObject(id: Int = 0,
scope: LTree,
contextId: Int,
kind: String,
shadowId: Int,
formId: Int,
commitId: Int,
updatedAt: Instant = Instant.now,
createdAt: Instant = Instant.now,
archivedAt: Option[Instant] = None)
extends FoxModel[GenericObject]
with Validation[GenericObject]
with ObjectHead[GenericObject] {

def withNewShadowAndCommit(shadowId: Int, commitId: Int): GenericObject =
this.copy(shadowId = shadowId, commitId = commitId)
}

class GenericObjects(tag: Tag) extends ObjectHeads[GenericObject](tag, "generic_objects") {

def kind = column[String]("kind")

def * =
(id, scope, contextId, kind, shadowId, formId, commitId, updatedAt, createdAt, archivedAt) <>
((GenericObject.apply _).tupled, GenericObject.unapply)
}

object GenericObjects
extends FoxTableQuery[GenericObject, GenericObjects](new GenericObjects(_))
with ReturningId[GenericObject, GenericObjects] {

val returningLens: Lens[GenericObject, Int] = lens[GenericObject].id

def filterByContext(contextId: Int): QuerySeq =
filter(_.contextId === contextId)

def filterByFormId(formId: Int): QuerySeq =
filter(_.formId === formId)

def withContextAndGenericObject(contextId: Int, genericObjectId: Int): QuerySeq =
filter(_.contextId === contextId).filter(_.formId === genericObjectId)

def withContextAndForm(contextId: Int, formId: Int): QuerySeq =
filter(_.contextId === contextId).filter(_.formId === formId)

def withContextAndKind(contextId: Int, kind: String): QuerySeq =
filter(_.contextId === contextId).filter(_.kind === kind)
}
5 changes: 3 additions & 2 deletions phoenix-scala/app/models/objects/IlluminatedObject.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ case class IlluminatedContext(name: String, attributes: Json)
* An IlluminatedObject is what you get when you combine the product shadow and
* the product.
*/
case class IlluminatedObject(id: Int = 0, attributes: Json) extends Identity[IlluminatedObject]
case class IlluminatedObject(id: Int = 0, kind: String, attributes: Json)
extends Identity[IlluminatedObject]

object IlluminatedObject {

def illuminate(form: ObjectForm, shadow: ObjectShadow): IlluminatedObject = {
val attributes = IlluminateAlgorithm.projectAttributes(form.attributes, shadow.attributes)
IlluminatedObject(id = form.id, attributes = attributes)
IlluminatedObject(id = form.id, kind = form.kind, attributes = attributes)
}
}
13 changes: 13 additions & 0 deletions phoenix-scala/app/payloads/GenericObjectPayloads.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package payloads

import utils.aliases._

object GenericObjectPayloads {

case class CreateGenericObject(kind: String,
attributes: Map[String, Json],
schema: Option[String] = None,
scope: Option[String] = None)

case class UpdateGenericObject(attributes: Map[String, Json])
}
4 changes: 2 additions & 2 deletions phoenix-scala/app/responses/ObjectResponses.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ object ObjectResponses {

object IlluminatedObjectResponse {

case class Root(id: Int, attributes: Json)
case class Root(id: Int, kind: String, attributes: Json)

def build(s: IlluminatedObject): Root =
Root(id = s.id, attributes = s.attributes)
Root(id = s.id, kind = s.kind, attributes = s.attributes)
}

object ObjectSchemaResponse {
Expand Down
Loading