Adds Dashboard controller.
authorDavid Schoonover <dsc@wikimedia.org>
Mon, 18 Jun 2012 23:00:32 +0000 (16:00 -0700)
committerDavid Schoonover <dsc@wikimedia.org>
Mon, 18 Jun 2012 23:00:32 +0000 (16:00 -0700)
lib/dashboard/dashboard-model.co
lib/server/controllers/dashboard.co
lib/server/file-controller.co [new file with mode: 0644]
lib/server/server.co
www/modules.yaml

index 35b1d2f..4ae98dc 100644 (file)
  */
 Dashboard = exports.Dashboard = BaseModel.extend do # {{{
     urlRoot  : '/dashboards'
-    graphs : []
     
     
     constructor: function Dashboard
         BaseModel ...
     
     initialize: ->
-        @graphs = []
         BaseModel::initialize ...
-        
+    
     
     defaults: ->
-        {}
+        name : null
+        graphs : []
     
     
 # }}}
index e69de29..ca6c844 100644 (file)
@@ -0,0 +1,91 @@
+fs         = require 'fs'
+path       = require 'path'
+{existsSync:exists} = path
+
+_          = require 'underscore'
+Seq        = require 'seq'
+yaml       = require 'js-yaml'
+{mkdirp, mkdirpAsync} = require '../mkdirp'
+
+{ readJSONFilesAsync,
+} = require '../files'
+
+FileBackedController = require '../file-controller'
+
+
+/**
+ * @class Resource controller for dashboard requests.
+ */
+class DashboardController extends FileBackedController
+    PROTECTED_IDS : <[
+        main
+        reportcard
+    ]>
+    PROTECT : true
+    
+    name    : 'dashboards'
+    dataDir : 'data/dashboards'
+    -> super ...
+    
+    
+    
+    # GET /dashboards.:format?
+    index: (req, res) ->
+        # if format is json, then return the dashboard JSON
+        switch req.format
+            case 'json'
+                pattern = @dataDir + "/*.json"
+                Seq()
+                    # find list of data/dashboard/*.json files, send back the objects
+                    .seq -> readJSONFilesAsync pattern, this
+                    # remove filepath info
+                    .seq (files) -> res.send _.values files
+            default 
+                res.render "#{@id}/index"
+    
+    # GET /dashboards/:dashboard
+    show: (req, res) ->
+        if req.format is 'json'
+            res.send req.dashboard
+        else
+            res.render "#{@id}/view"
+    
+    # GET /dashboards/:dashboard/edit
+    edit: (req, res) ->
+        if req.format is 'json'
+            res.send req.dashboard
+        else
+            res.render "#{@id}/edit"
+    
+    # GET /dashboards/new
+    new: (req, res) ->
+        res.render "#{@id}/edit"
+    
+    # POST /dashboards
+    create: (req, res) ->
+        return unless data = @processBody req, res
+        file = @toFile data.id
+        if exists file
+            return res.send { result:"error", message:"Dashboard '#{data.id}' already exists!" }
+        else
+            fs.writeFile file, JSON.stringify(data), "utf8", @errorHandler(res, "Error writing Dashboard!")
+    
+    # PUT /dashboards/:dashboard
+    update: (req, res) ->
+        return unless data = @processBody req, res
+        if @PROTECT and _ @PROTECTED_IDS .contains data.id
+            return res.send {result:"error", message:"Dashboard '#{data.id}' is read-only."}, 403
+        fs.writeFile @toFile(data.id), JSON.stringify(data), "utf8", @errorHandler(res, "Error writing Dashboard!")
+    
+    # DELETE /dashboards/:dashboard
+    destroy: (req, res) ->
+        id = req.param.dashboard
+        if @PROTECT and _ @PROTECTED_IDS .contains id
+            return res.send {result:"error", message:"Dashboard '#{id}' is read-only."}, 403
+        fs.unlink @toFile(id), @errorHandler(res, "Dashboard '#{id}' does not exist!")
+    
+    
+
+
+
+module.exports = exports = DashboardController
diff --git a/lib/server/file-controller.co b/lib/server/file-controller.co
new file mode 100644 (file)
index 0000000..f4d29c7
--- /dev/null
@@ -0,0 +1,95 @@
+fs         = require 'fs'
+path       = require 'path'
+{existsSync:exists} = path
+
+_          = require 'underscore'
+Seq        = require 'seq'
+yaml       = require 'js-yaml'
+{mkdirp, mkdirpAsync} = require './mkdirp'
+
+{ readJSONFilesAsync,
+} = require './files'
+
+Controller = require './controller'
+
+
+/**
+ * @class Resource controller backed by flat json or yaml files.
+ */
+class FileBackedController extends Controller
+    
+    name    : null
+    dataDir : null
+    
+    ->
+        @dataDir or= "data/#{@name}"
+        super ...
+    
+    
+    
+    /**
+     * Override to customize lookup of files by ID.
+     * 
+     * @param {String} id ID of this resource.
+     * @returns {String} Path to file for this resource.
+     */
+    toFile: (id) -> "#{@dataDir}/#id.json"
+    
+    
+    /**
+     * Auto-load :id for related requests.
+     * 
+     * @param {String} id ID of the resource.
+     * @param {Function} cb Callback to invoke with the loaded object.
+     */
+    autoload: (id, cb) ->
+        file = @toFile id
+        parser = JSON.parse
+        
+        yamlFile = file.replace /\.json$/i, '.yaml'
+        if exists yamlFile
+            file = yamlFile
+            parser = yaml.load
+        
+        err, data <- fs.readFile file, 'utf8'
+        
+        if 'ENOENT' is err?.code
+            return cb null, {}
+        if err
+            console.error "#{this}.autoload(#id, #{typeof cb}) -->\nerr"
+            return cb err
+        try
+            cb null, parser data
+        catch err
+            console.error "#{this}.autoload(#id, #{typeof cb}) -->\nerr"
+            cb err
+    
+    ### Helpers
+    
+    processBody: (req, res) ->
+        if not req.body
+            res.send {result:"error", message:"Data required!"}, 501
+            return false
+        
+        data = req.body
+        data.slug or= data.id
+        data.id or= data.slug
+        
+        if not data.slug
+            res.send {result:"error", message:"Slug required!"}, 501
+            return false
+        
+        mkdirp @dataDir if not exists @dataDir
+        return data
+    
+    errorHandler: (res, msg) ->
+        (err) ->
+            if err
+                msg or= err.message or String(err)
+                console.error msg
+                res.send { result:"error", message:msg }, 501
+            else
+                res.send { result:"ok" }
+
+
+module.exports = exports = FileBackedController
index 67bc809..bb01baa 100755 (executable)
@@ -145,6 +145,7 @@ app.configure ->
 
 Controller   = require './controller'
 app.controller require './controllers/graph'
+app.controller require './controllers/dashboard'
 
 YAML_EXT_PAT = /\.ya?ml$/i
 app.get '/datasources/all', (req, res, next) ->
index 371a1e2..c11206c 100644 (file)
@@ -65,8 +65,10 @@ dev:
                     - graph-list.jade
             - util:
                 - underscore:
+                    - function
                     - array
                     - object
+                    - class
                     - kv
                     - string
                     - index
@@ -88,6 +90,7 @@ dev:
                     - scaffold-model
                     - scaffold-view
                     - index
+                - base
                 - base-mixin
                 - base-model
                 - base-view