Skip to content

Commit

Permalink
Merge pull request #10 from guhmerces/feat/inital_structure
Browse files Browse the repository at this point in the history
Feat/inital structure
  • Loading branch information
rjrodger authored Feb 7, 2022
2 parents 470ed3e + dd2b50b commit 6226638
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 86 deletions.
19 changes: 19 additions & 0 deletions src/entities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { EntityMap } from "./types";

const entities: EntityMap = {
event: {
actions: {
load: {
request: {
method: "get",
path: "/events/:event_id/",
},
before: [
{ on:'query', field: 'attribute', set: { query: 'event_id' } }
]
}
},
}
}

export { entities }
107 changes: 63 additions & 44 deletions src/eventbrite-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import Eventbrite from 'eventbrite'
import { Sdk } from 'eventbrite/lib/types'
import { entities } from './entities'
import { make_actions } from './make-actions'
import { make_request } from './make-request'
import { ActionData, EntityMap } from './types'

type EventbriteProviderOptions = {}

Expand All @@ -13,63 +17,78 @@ function EventbriteProvider(this: any, options: any) {

let eventbrite: Sdk

seneca
.message('role:entity,cmd:load,zone:provider,base:eventbrite,name:event', loadEvent)
.message('role:entity,cmd:save,zone:provider,base:eventbrite,name:event', saveEvent)


seneca.prepare(async function(this: any) {
let out = await this.post('sys:provider,get:key,provider:eventbrite,key:api')
if (out.ok) {
API_KEY = out.value
eventbrite = Eventbrite({ token: API_KEY })
add_actions()

function add_actions() {
const actions = prepare_actions(entities)

for (const action of actions) {
switch (action.pattern.cmd) {
case 'load':
seneca.message(action.pattern, make_load(action))
break

case 'save':
seneca.message(action.pattern, make_save(action))
break
}
}
else {
this.fail('api-key-missing')
}
})

}

async function loadEvent(this: any, msg: any) {
const q: any = msg.q
const eventID: string = q.id
function make_load(action: ActionData) {
return make_actions(action)['load']
}

try {
const event: any = await eventbrite.request(`/events/${eventID}`)
const ent = this.make$('provider/eventbrite/event').data$(event)
return ent
}
catch (e: any) {
// TODO: better error description
throw new Error('Eventbrite Error: ' + JSON.stringify(e.parsedError))
}
function make_save(action: ActionData) {
return make_actions(action)['save']
}


async function saveEvent(this: any, msg: any) {
const ent: any = msg.ent
const eventID: string = ent.id
// prepare links replacing placeholders
function prepare_actions(entities: EntityMap): ActionData[] {
const actions_details: ActionData[] = []

const body = JSON.stringify({
event: {
description: {
html: ent.summary,
},
},
})
for(const [ent_name, ent_details] of Object.entries(entities)) {
ent_details.name = ent_name

// Missing a slash at the end of the URL cause the Fetch API to not handle POST requests correctly.
const event: any = await eventbrite.request(`/events/${eventID}/`, {
method: 'POST',
body,
})
for(const [action_name, action_data] of Object.entries(ent_details.actions)) {

const out = this.make$('provider/eventbrite/event').data$(event)
const pattern = {
name: ent_name,
cmd: action_name,
zone: 'provider',
base: 'eventbrite',
role: 'entity',
}

console.log('SAVE', out)
actions_details.push({
pattern,
req_fn: prepare_req_fn(action_data.request.method),
...action_data
})
}

return out
}
return actions_details
}

function prepare_req_fn(method: string) {
return (path: string, options?: Record<string, any>) => {
return make_request(eventbrite.request, path, method, options)
}
}

seneca.prepare(async function(this: any) {
let out = await this.post('sys:provider,get:key,provider:eventbrite,key:api')
if (out.ok) {
API_KEY = out.value
eventbrite = Eventbrite({ token: API_KEY })
}
else {
this.fail('api-key-missing')
}
})
}


Expand Down
59 changes: 59 additions & 0 deletions src/make-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ActionData } from "./types"
import { perform_tasks } from "./utils"

function make_actions(action_data: ActionData) {
const { req_fn, request, after, before } = action_data
const { path } = request

async function load(this:any, msg:any) {
const { q } = msg

const context = {
query: q,
}

if(before) {
perform_tasks(before, context)
}

const built_path = build_path(path, q)

const res = await req_fn(built_path)

const outent = this.make$(msg.ent.entity$).data$(res)

if(after) {
perform_tasks(after, {
res,
outent,
...context
})
}

return outent
}

async function save(this:any, msg:any) {

}

function build_path(path: string, args: Record<string, any>) {
const placeholders = path
.split("/")
.filter(p => p.match(":(.[^/]*)")) // matches against sentences like :foo

placeholders.forEach(p => {
const param_name = p.split(":")[1]
path = path.replace(p, args[param_name] ?? p)
})

return path
}

return {
load,
save
}
}

export { make_actions }
20 changes: 20 additions & 0 deletions src/make-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export async function make_request(reqFn: CallableFunction, path: string, method: string, op?: any) {
try {

if(op) {
const res = await reqFn(path, {
method,
...op
})
return res
}

const res = await reqFn(path)
return res

} catch (error: any) {
console.log(error)
// TODO: better error description
throw new Error('Eventbrite Error: ' + JSON.stringify(error.parsedError))
}
}
53 changes: 53 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
type EntityMap = {
[name: string]: EntDetails
}

type Actions = "load" | "save"

type ReqDetails = {
method: string
path: string
body_spec?: Record<string,any>
}

type Task = {
on: keyof Context
field: string
set?: Set
}

type Set = {
[key in keyof Context]: string
}

type Context = {
outent?: any
inent?: any
req?: any
res?: any
query?: any
}

type ActionDetails = {
request: ReqDetails
after?: Task[]
before?: Task[]
}

type EntDetails = {
name?: string
actions: {
[action in Actions]?: ActionDetails
}
}

type TasksTypesFn = {
set: (task: Task, context: Context) => void
}

interface ActionData extends ActionDetails {
pattern: Record<string,string>
req_fn: (path:string, options?: Record<any,string>) => Promise<any>
}

export type { EntityMap, EntDetails, ActionData, Task, Context, TasksTypesFn }
49 changes: 49 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Context, Task, TasksTypesFn } from "./types"

function perform_tasks(tasks: Task[], context: Context ) {
tasks.forEach(task => {
const [_, __, ...types] = Object.keys(task)

types.forEach(type => {
const typeFn = tasksTypes[type as keyof TasksTypesFn]

if(!typeFn) {
throw new Error('unable to find task of type ' + type)
}

typeFn(task, context)
})
})

return context
}

function set(task: Task, context: Context) {
if(!task.set) {
return
}

const source_name = Object.keys(task.set)[0]

if(!source_name) {
throw new Error('A source object is required when setting a target')
}

const target = context[task.on]
const target_field = task.field

const source = context[source_name as keyof Context]
const source_field: any = task.set[source_name as keyof Context]

target[target_field] = source[source_field]
}

const tasksTypes: TasksTypesFn = {
set
}

export { perform_tasks };

export type {
Task
}
Loading

0 comments on commit 6226638

Please sign in to comment.