Mostrando las entradas con la etiqueta javascript. Mostrar todas las entradas
Mostrando las entradas con la etiqueta javascript. Mostrar todas las entradas

2016-07-08

Sandro en otros contextos

Hasta hace 5 minutos sandro sólo andaba si el WAR que deployás lo deployás en el contexto ROOT. Ahora ya no es más así. A partir de ahora es posible correr sandro con toda su magia para compartir código entre cliente y server desde cualquier contexto. Eso permite que haya más de una aplicación de sandro en el mismo Tomcat.

Enjoy!

2016-02-08

Regalos de carnaval para sandro

Hice 2 arreglos en sandro.

  • Cambié la forma de usar rhino y ahora puede procesar archivos grandes de javascript de nuevo. Eso hace que ande d3 en el server nuevamente.
  • Ahora los archivos .js que usa sandro tienen que estar encodeados en utf-8, permitiendo escribir en el código cosas como ( ͡° ͜ʖ ͡°).
Happy hacking,
Aureliano.

2016-01-31

Ciclo completo para autenticarse por oauth2 en línea de comando

Hace un tiempito que estoy jugando con el API de blogger para hacer posts automaticamente. Lo que me parece más complicado es autenticarse vía oauth2 desde una aplicación de línea de comando. Por eso hice un módulo de sandro que lo hace por mi, pidiendo el código de autorización solo cuando es necesario

Se puede usar como módulo de sandro así:

define({
  accessToken: './accessToken'
},
function(m) {
  var accessToken = m.accessToken(
    'refresh_token_fname.txt', 
    {
      clientId: 'CLIENT ID HERE',
      clientSecret: 'CLIENT SECRET HERE'
    }
  )
  // Use accessToken here!
})

Abajo les dejo accessToken.js completo (cuidado, son 134 líneas de código):

define({
  javaPackages: "javaPackages",
  ary: "sandro/nadaMas/array",
  json: "sandro/nadaMas/json",
  object: "sandro/nadaMas/object"
}, function(m) {
 
  var URL = m.javaPackages.java.net.URL
  var String = m.javaPackages.java.lang.String
  var URLEncoder = m.javaPackages.java.net.URLEncoder
  var StandardCharsets = m.javaPackages.java.nio.charset.StandardCharsets
  var InputStreamReader = m.javaPackages.java.io.InputStreamReader
  var BufferedReader = m.javaPackages.java.io.BufferedReader
  var Scanner = m.javaPackages.java.util.Scanner
  var System = m.javaPackages.java.lang.System
  var File = java.io.File
  var FileOutputStream = java.io.FileOutputStream
  var OutputStreamWriter = java.io.OutputStreamWriter
  var out = System.out
  var in_ = new Scanner(System["in"])

  var paramBytes = function(p) {
    var s = new String( Object.keys(p).map(function(k) {
      return "" + URLEncoder.encode(k, "UTF-8") + "=" + URLEncoder.encode(p[k])
    }).join("&") )
    return s.getBytes(StandardCharsets.UTF_8)
  }
 
  var postJsonResult = function(url, params) {
    var parameters = paramBytes(params)
    var c = new URL(url).openConnection()
    try {
    c.requestMethod = "POST"
    c.doOutput = true
    c.fixedLengthStreamingMode = parameters.length
    c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    c.connect()
    c.outputStream.write(parameters)
  
    if (200 != c.responseCode) {
      return null
    }
    var response = ""
    var reader = new BufferedReader( new InputStreamReader( c.inputStream ) )
  
    while( true ) {
      var buff = reader.readLine()
      if (buff == null) {
        break
      }
      response += buff
    }
    return m.json.parse( "" + response )
    } finally {
      c.disconnect()
    }
  
  }
  var authCode2refreshToken = function(authCode, credentials) {
    var rv = postJsonResult(
      "https://www.googleapis.com/oauth2/v4/token", {
        code: authCode,
        redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
        client_id: credentials.clientId,
        client_secret: credentials.clientSecret,
        grant_type: "authorization_code"
      }
    )
  
    return rv ? rv["refresh_token"] : null
  }
 
  var refreshAccessToken = function(refreshToken, credentials) {
    var rv = postJsonResult(
      "https://www.googleapis.com/oauth2/v4/token", {
        refresh_token: refreshToken,
        client_id: credentials.clientId,
        client_secret: credentials.clientSecret,
        grant_type: "refresh_token"
      }
    )
 
    return rv ? rv["access_token"] : null
  }
 
  var readFileLine = function(fname) {
    var sc = new Scanner(new File(fname))
    try {
      return sc.nextLine()
    } finally {
      sc.close()
    }
  }
 
  var writeFileLine = function(fname, line) {
    try {
      var w = new OutputStreamWriter( new FileOutputStream(fname) )
      line = "" + line
      w.write(line, 0, line.length)
    } finally {
      w.close()
    }
  }
 
  var obtainAccessToken = function(refreshTokenFname, credentials) {
    var accessToken = null
    var refreshToken = null
    try {
      refreshToken = readFileLine(refreshTokenFname)
    } catch (e) {
      // ignore
    }
  
    while (true) {
      if (refreshToken) {
        accessToken = refreshAccessToken(refreshToken, credentials)
      }
      if (accessToken) {
        return accessToken
      }
    
      var authorizationUrl =
        "https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=" +
        credentials.clientId
      out.println("Anda a este URL, completa la autorizacion y pega el codigo de autorizacion aca. Despues presiona ENTER")
      out.println(authorizationUrl)
      var authCode = in_.nextLine()
      refreshToken = authCode2refreshToken(authCode, credentials)
      writeFileLine(refreshTokenFname, refreshToken)
    }
  }
 
  return obtainAccessToken
})



Si quieren, es bastante fácil transliterarlo a código Java.

Espero que les sirva,
Aureliano.

2016-01-30

Obteniendo el access token

Sigo en mi cruzada para hacer cosas automáticamente en blogger usando sandro. En este caso, hice el código para obtener un access token a partir de un authorization code. Para eso, primero necesitás registrar una aplicación de línea de comando. Una vez que lo hayas hecho, vas a tener el client id y el client secret. Pero eso no alcanza, para obtener el access token, como el que usamos en el post anterior, hace falta un authorization code. Para obtenerlo, conectate a este URL:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=ACA_VA_TU_CLIENT_ID

Y te va a mostrar el authorization code después de loguearte a google y autorizar a tu aplicación a hacer cosas de blogger en tu nombre.

Una vez que obtuviste tu authorization code, hay que obtener un access token. El access token te sirve para manipular blogger con tu usuario por la próxima hora. Para renovarlo, hay que usar un refresh token, que también obtendremos, pero voy a mostrar cómo usarlo en otro post.

Acá abajo dejo el código de ejemplo para obtener estos tokens.

define({
  javaPackages: "javaPackages",
  ary: "sandro/nadaMas/array",

  json: "sandro/nadaMas/json",
  object: "sandro/nadaMas/object"
}, function(m) {
 
  var URL = m.javaPackages.java.net.URL
  var out = m.javaPackages.java.lang.System.out
  var String = m.javaPackages.java.lang.String
  var URLEncoder = m.javaPackages.java.net.URLEncoder
  var StandardCharsets = m.javaPackages.java.nio.charset.StandardCharsets
  var InputStreamReader = m.javaPackages.java.io.InputStreamReader
  var BufferedReader = m.javaPackages.java.io.BufferedReader
 
  var tokenUrl = new URL("https://www.googleapis.com/oauth2/v3/token")

  var paramBytes = function(p) {
    var s = Object.keys(p).map(function(k) {
      return "" + URLEncoder.encode(k, "UTF-8") + "=" + URLEncoder.encode(p[k])
    }).join("&")
    out.println(s)
    s = new String(s)
    return s.getBytes(StandardCharsets.UTF_8)
  
  }
 
  return function(code, client_id, client_secret) {
    var parameters = paramBytes({
      code: code,
      redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
      client_id: client_id,
      client_secret: client_secret,
      grant_type: "authorization_code"
    })
    var c = tokenUrl.openConnection()
    c.requestMethod = "POST"
    c.doOutput = true
    c.fixedLengthStreamingMode = parameters.length
    c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    c.connect()
    c.outputStream.write(parameters)
  
    var response = ""
    var reader = new BufferedReader( new InputStreamReader( c.inputStream ) )
  
    while( true ) {
      var buff = reader.readLine()
      if (buff == null) {
        break
      }
      response += buff
    }
    var jsonResponse = m.json.parse( "" + response )

    out.println("Tokens: " + m.json.stringify(jsonResponse))

  }
})


No uso la google-client-api de java porque es un quilombo y no entendí cómo usarla, así que me conecto a los webservices de google directamente.


2016-01-24

Accediendo al API de blogger desde sandro

Hoy estuve jugando con el API de blogger desde un script que hice usando sandro. Como puedo usar las APIs de java desde el código JavaScript que implementa los módulos de Sandro, decidí usar la biblioteca que google provee para Java.
Lo que más me costó fue toda la parte de oauth2. Como estoy solo probando, no quise armar todo un webserver para hacer todo el camino de oauth. En vez de eso, usé el oauth playground para obtener un access-token y programé desde ahí.

Con todo esto, y afanando bastante de acá hice un scriptcito que obtiene algunos datos de este blog.

define({
  javaPackages : "javaPackages",
  ary : "sandro/nadaMas/array"

}, function(m) {
 
  var Blogger = m.javaPackages.com.google.api.services.blogger.Blogger
  var NetHttpTransport = m.javaPackages.com.google.api.client.http.javanet.NetHttpTransport
  var JacksonFactory = com.google.api.client.json.jackson2.JacksonFactory
  var GoogleCredential = com.google.api.client.googleapis.auth.oauth2.GoogleCredential

  var HTTP_TRANSPORT = new NetHttpTransport();
  var JSON_FACTORY = new JacksonFactory();
  var CREDENTIAL = GoogleCredential().setAccessToken("HERE GOES THE ACCESS TOKEN")
 
  var buildBlogger = function() {
    var bb = new Blogger.Builder(HTTP_TRANSPORT, JSON_FACTORY, CREDENTIAL)
    bb.applicationName = "Testing blogger"
    return bb.build()
  }

  return function() {
    var out = m.javaPackages.java.lang.System.out
   
    var blogger = buildBlogger()
   
    var action = blogger.blogs().getByUrl("http://aurelianito.blogspot.com")
 

    action.fields = "description,name,posts/totalItems,updated"

    var blog = action.execute();

    // Now we can navigate the response.
    out.println("Name: " + blog.name)
    out.println("Description: " + blog.description)
    out.println("Post Count: " + blog.posts.totalItems)
    out.println("Last Updated: " + blog.updated)
   
  }

})


Si lo corren debería mostrarles algo muy parecido a:

Name: aurelianito
Description: Blog de Aureliano Calvo.
Aureliano Calvo's blog.
Post Count: 318
Last Updated: 2016-01-20T19:49:14.000-03:00


Después tengo que ver cómo hacer para hacer que el flujo del oauth ande bien.

Happy hacking,
Aureliano

2015-12-11

Cambio en handlers de sandro

Hasta hace un ratito, un handler era una función que tomaba un request y un response. Ahora en vez de tomar esos 2 parámetros toma un diccionario. Los campos request y response tienen lo mismo de antes, pero ahora podemos en el encadenamiento de handlers agregar información. Por ejemplo, el json parseado de un request y/o el usuario que obtuvimos de la autenticación van a estar también en ese diccionario cuando sea apropiado.

Proximamente voy a estar agregando más cosas que andan por ahí.

Happy hacking,
Aureliano.


2015-10-19

Tengo, queries a la base de datos en sandro

En los últimos días implementé el módulo sandro/tengo. Lo que hice fue una forma "objetosa" de contruir queries de SQL y procesar los resultados, usando como base JDBC. La idea es evitar los problemas de seguridad por inyección que plagan los usos de las bases de datos sin restringir la funcionalidad. El código que hice lo probé con sqlite3, y debería andar con cualquier base de datos que sea ANSI-SQL y tenga driver de JDBC.

Acá les dejo un ejemplo de cómo hacer un query usando tengo. Asumiendo que connection es una conexión a la base de datos y hay una tabla person (id integer, name string, rank integer), así hago un query:

define(
  {
    tengo: "sandro/tengo"
  },
  function(m) {

    // Some code before, constructing the JDBC connection

    var _ = m.tengo.ansiBuilder

    // SELECT * FROM person ORDER BY id DESC;
    var results = _.select({
      from: "person",
      order_by: _.desc("id")
    }).map(connection, function(rs) {
      return {

        name: "" + rs.getString("name"), 
        id: rs.getInt("id")
      }
    })


    // SELECT count(1) as c, rank FROM person GROUP BY rank HAVING c > 1
    var results2 = _.select({
      columns: [ _.alias("c",_.invoke("count", _.int(1))), "rank" ],
      from: "person",
      group_by: "rank",
      having: _.gt("c", _.int(1))
    }).map(connection, function(rs) {
      return {

        c: rs.getInt("c"), 
        rank:rs.getInt("rank")
      }
    })
 


    // You can process the results here
  }
)

En la implementación actual podés hacer SELECTs con un montón de opciones (TODO: documentar todo). La idea subyacente es que las queries se arman haciendo árboles de objetos que generan los queries con objetos inmutables siguiendo la sintaxis de SQL (para que puedan usar los conocimientos de SQL que ya tienen), y estos objetos se encargan de escapar el SQL. Para que sea menos verbose, los strings son transformados en símbolos de SQL (como columnas o tablas), los números en expresiones que representan el número en punto flotante y los arrays en expresiones separadas por comas, que está buenísimo para hacer queries con varias tablas, requerir varias columnas o hacer ORDER BY con varios criterios.

Lo siguiente que toca es hacer que se pueda hacer UPDATE, DELETE, INSERT. Y después toca CREATE TABLE y DROP TABLE.

¿Qué frameworks conocen que les dejen hacer queries tan fácilmente? Lo más parecido que se me ocurre es el API de bajo nivel de SQLAlchemy, pero como los elementos de las queries son stateful, es mucho más quilombo.

2015-09-25

Al final sandro tiene basic authentication

Sandro tiene soporte para basic authentication. Para usarlo hay que importar el módulo sandro/alFinal. Se usa así:

alFinal.basicAuth( my_handler, function(user, pass) { return user === pass }, "sample realm")

Los tres parámetros que recibe son:

  • Un handler para ejecutar si la autenticación funciona. Es una función con 3 parámetros; request, response y user (un string)  
  • Una función para decidir si el acceso es autorizado o no, recibe usuario y password.
  • Un realm (string)
La implementación del feature está en este commit.

Happy hacking,
Aureliano.

2015-09-11

2 nuevos features para sandro

Recientemente le agregué 2 nuevos features a sandro.

  1. alFinal.fullJson es una utilidad para manejar servicios que reciben en el body (por POST) un request en JSON y devuelven otro JSON. Para implementar el servicio le pasás una función, que recibe 3 parámetros: jsonRequest (objecto con el request del body), request (el objeto request del servlet) y response (el objeto response del servlet). La función tiene que retornar un objeto JavaScript a ser JSONificado para mandar al cliente
  2. El web.xml soporta el parámetro do-not-reload para el RhinoServlet. Ahí pasás una lista de módulos, separados por comas, que no querés que se recarguen cuando llega el request.
Happy hacking,
Aureliano.

2015-08-23

Sandro tiene json al final

Le agregué a sandro en el módulo alFinal,  que es el que tiene todas las utilidades para hacer handlers, una utilidad para hacer webservices que devuelven json.

Para usarlo, hay que hacer algo como:
alFinal.json( function(request) {
  return { "it worked": true, echo: request.getParameter("echo") })

Por supuesto que pueden hacer la función que prefieran ;).

La implementación de la utilidad para hacer json es re complicada. Acá les dejo el código completo :p

function(f) {
  return function(request, response) {
    response.setContentType("application/json")
    response.writer.print( m.json.stringify( f(request, response) ) )
  }
}


Happy hacking,
Aureliano.

2015-06-23

Sandro en la consola

En los últimos días hice que sea posible correr la biblioteca de sandro de línea de comando y tenga una consola. Para correr la consola, usando todos los módulos de sandro, ponelo en el classpath (con sus dependencias) y corré:

$ java sandro.RhinoConsole

Eso abre una consola donde podés correr código, que es una consola como la de rhino pero customizada para poder importar los módulos de sandro usando penumbras.

js> var funMod = penumbras.loadModule("sandro/nadaMas/function")
js> funMod.isFun(5)
false
js> funMod.isFun(function(a) {} )
true


Pero esto no es todo, ¡también se pueden correr módulos de sandro desde la línea de comandos! Para eso, hay que poner los módulos en el classpath (en un jar o un directorio), aparte de sandro y sus dependencias, y correr:

$ java sandro.RhinoRunner my/custom/module param1 param2

Para que corran, el módulo tiene que exportar una función, que es la que llama el runner. Pueden mirar el módulo helloWorld (está en el proyecto de la biblioteca por ahora) para ver un ejemplo tonto:

define({
  javaPackages : "javaPackages",
  ary : "sandro/nadaMas/array"
}, function(m) {

  return function() {
    var out = m.javaPackages.java.lang.System.out
    out.println("Hello!")
    out.println(m.ary.fromArgs(arguments).join())
  }

})


Y si lo invocan de línea de comando, pasa esto:

$ java sandro.RhinoRunner helloWorld Hola Manola
Hello!
Hola,Manola


Usando esto, hice que los tests de la biblioteca corran de línea de comando así:

$ java sandro.RhinoRunner sandro/testRunner

Por último, saqué la dependencia de servlets del cargador de módulos, así que ahora no hace falta tener el API de servlets en el classpath para usar todas las cosas que no tienen que ver con la web.

Happy hacking,
Aureliano.

2014-11-01

Como lo hice yo

Estuve avanzando y empecé un módulo nuevo en sandro. Se llama comoLoHiceYo en honor a la canción de Sandro con el mismo nombre y tiene las configuraciones "opinionated" que hacen falta para unir cosas y que sea fácil compartir código entre cliente y server. Antes era posible, ahora es una pavada.
Éste es el código de una página de ejemplo que corre un pedacito en el cliente y otro en el server. Primero en el server, app/endpoint/clientCode.js es así:
define([], function() {
  return function(params) {
    var d3 = params.d3
    var startClientJs = params.startClientJs
   
    d3.select("body").append("div").text("Generated on the server")
    startClientJs("app/shared/clientCode")
  }
})

Y para el cliente está app/shared/clientCode.js:
define(["external/d3"], function(d3) {
  d3.select("body").append("div").text("Generated on the client")
})

Ya se empieza a ver que hay un montón de cosas que se pueden hacer en los dos lados. En este ejemplo, uso d3 tanto en el cliente como en el server para manipular el DOM.
¿Querés usar sandro? Decime y te ayudo.

2014-10-26

Novedades en sandro

Tengo varias novedades de sandro. La primera es que separé el desarrollo del site de la biblioteca. Para eso hice el proyecto sandro-lib, donde tiene el proyecto sandro y sus tests. Por lo tanto, el proyecto queda así:

  • sandro: Site principal. Corre los tests. En un futuro va a tener toda la documentación. Es el primer site hecho en sandro y siempre va a usar la última versión. Si tenés alguna duda sobre cómo hacer algo, podés mirar el código acá.
  • sandro-lib: Biblioteca de base para hacer sites con sandro
  • domino: Port del proyecto domino para correr en sandro
La segunda es que hice mi primer visualización en d3 corriendo en el server, como parte de las cosas que hice en el Hackaton de la fundación Sadosky sobre agro. Lo siento, no tengo commit de esto por ahora.

La tercera es que cambié la forma de exponer los scripts que se ejecutan en el cliente. Ahora hay una acción del módulo alFinal que expone los scripts, tomando por parámetro una función que decide si un módulo puede ser expuesto dado el nombre de su recurso. Acá el ejemplo de cómo anda:
      router = c.camino()
      router.register(c.prefixPath("/js"), alFinal.jsCode({
        published: function(resource) {
          resource = "" + resource
          return (
            resource.startsWith("/sandro/") ||
            resource.startsWith("/app/shared/") ||
            resource.startsWith("/external/")
          )
        },
        prefix: "/js"
      }))

Muchos avances en muy poco tiempo. Espero poder seguir así,
Aureliano

2014-10-20

D3 en sandro

Basándome en la integración a sandro de domino, y el módulo comoTeDire, ¡hice andar d3 en el server en sandro! Con esto espero que podamos generar SVGs y visualizaciones indistintamente en cliente y server, y también quiero basar el sistema de templating de sandro en eso.
¿Alguien quiere financiar el proyecto? ¡Ya sandro hace cosas que ningún otro framework hace!

2014-10-17

Domino en sandro!

Acabo de hacer el primer POC de manejo del DOM en el server con el mismo API que en el cliente. Para eso, porté domino a mi sistema de módulos e hice un POC.
¡Esto está tomando forma! ¿Cuántos frameworks web hay que puedas hacer código de presentación que corra tanto en el cliente o en el server?

¿Quién quiere ser early adopter?

2014-10-15

Módulos AMD en sandro

Ayer implementé en sandro módulos AMD, como los de requirejs. Esto debería hacer más fácil el uso de bibliotecas que usen commonjs. Acá les dejo el changeset donde está la implementación (con tests!).

Happy hacking,
Aureliano.

2014-09-21

Mejoras en sandro

Hice varias mejoras en sandro. Ahora se puede definir módulos que son endpoints y se cargan dinámicamente, definir rutas estáticas y ¡debuggear desde eclipse! Estos features llevaron sorprendentemente poco código.

Y ¡sigue pudiéndose compartir fácilmente código entre cliente y server! Ya está tomando forma y tiene cosas que no tienen la mayoría de los frameworks para hacer páginas web.

¿Cuándo hacés tu proyecto en sandro?,
Aureliano.

2014-08-19

Mi opinión sobre poner ; por todos lados en JavaScript

var betterWithSemicolons = function() {
    return
        true;
}

alert( betterWithSemicolons() ? "it is better with semicolons" : "semicolons can be quite misleading. Only use with care!" )

Yo programo en inglés :p.

2014-03-30

sandro reboot

Hace ya casi 4 años empecé un proyecto para hacer más fácil hacer páginas web seguras al que llamé sandro. Ahora estoy empezando el mismo proyecto de nuevo, siguiendo con mismas ideas rectoras:

  • Un solo lenguaje en todos lados, JavaScript.
  • Templating client-side y server side compartiendo código.
  • Validación de datos en el cliente y server compartiendo código.
  • Seguro por defecto. El diseño del framework debería hacer que sea más fácil hacer aplicaciones seguras que inseguras.
La primera versión va a estar basada en rhino, igual que la vez anterior, pero va a haber algunos cambios que creo que son importantes:
  • No va a estar enfocado a correr en app-engine.
  • Para mantener la idea de que haya JavaScript en todos lados, el motor de base de datos elegido va a ser mongodb.
  • Para reconciliarme con la naturaleza asíncrona y mono-thread del browser, voy a cambiar el manejo de módulos de commonjs por requirejs en el cliente y una API compatible con requirejs en el server.
Si quieren ir viendo el avance del proyecto, está en un proyecto público de bitbucket.

Espero feedback y, sobre todo, pull requests.

Happy hacking,
Aureliano.

2014-03-14

Selección de los hijos

En este post no estoy proponiendo al hijo del Kun para la selección de fútbol sino que quiero mostrar una solución a un problema puntual en d3. Hace un tiempito postié cómo hacer para hacer un selector de CSS que busque solo en los hijos de un nodo. El objetivo era hacer más robustas las selecciones de nodos en los gráficos que hago con d3. Lamentablemente ese truco no anda con un montón de browsers, pero encontré otro mucho más simple. Si queremos una selección con todos los nodos hijos de la selección actual, lo más fácil es hacer:

my_selection.selectAll(function() { return this.childNodes })

Al final era bastante fácil. No entiendo porqué esto no está en ningún lado. Casi siempre cuando uso selectAll quiero obtener los hijos de los nodos de la selección actual.

Happy hacking,
Aureliano.