WebDAV pseudo-PUT request code w/ YQL

My previous post presented the YQL code required to handle a WebDAV GET request for a “file” in yql storage. To update the file, we need an additional table.

Prerequisites:

  • A sherpa record w/ this value in it: {“file1″:”content 1″, “file2″, “content 2″}. The keys and values w/in the JSON can be anything. If you haven’t worked w/ YQL storage before, check out the documentation.
  • The code below edited to use your storage record’s select address

Flow:

  • The WebDAV client issues a PUT/POST request with “path” set to the key of the value to change, and “contents” set to the updated value.
  • The YQL table will extract the stored record, decode it, update the value, encode it, and store it back

Code:

<?xml version="1.0" encoding="UTF-8"?>
<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">
    <meta>
        <author>Erik Eldridge</author>
        <description>
        </description>
        <sampleQuery></sampleQuery>
    </meta>
    <bindings>
        <insert produces="XML">
            <inputs>
                <value id="path" type="xs:string" paramType="variable"/>
                <value id="contents" type="xs:string" paramType="variable"/>

            </inputs>
            <execute><![CDATA[
                var execute = 'store://{your store val}',
                    select = 'store://{your store val}',
                    update = 'store://{your store val}';
                
                // http://www.json.org/json2.js
                y.include('http://{your domain}/json2.js');
                    
                //credit: http://javascript.crockford.com/remedial.html
                if (typeof String.prototype.supplant !== 'function') {
                    String.prototype.supplant = function (o) { 
                        return this.replace(/{([^{}]*)}/g, 
                            function (a, b) {  
                                var r = o[b];
                                return typeof r === 'string' ? r : a; 
                            }); 
                    };
                }
                
                response.object = function () {

                    //put queries and results in arrays so we can reuse the var w/o overwriting values
                    var queries = [],
                        results = [];

                    queries[0] = 'select * from yql.storage where name="{select}"'.supplant({'select':select}),
                    results[0] = y.xmlToJson( y.query( queries[0] ).results );
                    
                    if (results[0].results.result.value[path]) {
                        
                        results[0].results.result.value[path] = contents;
                        
                        queries[1] = "update yql.storage set value='{json}' where name='{update}'"
                            .supplant({
                                'json' : JSON.stringify(results[0].results.result.value),
                                'update' : update
                            });
                        results[1] = y.xmlToJson( y.query( queries[1] ).results );

                        return {
                           "headers" : {
                               "HTTP/1.1 status" : "204",
                               "Date" : new Date().getTime(),
                               "Location" : "/webdav/" + path,
                               "Content-Length" : contents.length,
                               "Connection" : "close",
                               "Content-Type" : "text/plain; charset=UTF-8"
                           }
                        }
                    }

                    results[0].results.result.value[path] = contents;

                    queries[1] = "update yql.storage set value='{json}' where name='{update}'"
                        .supplant({
                            'json' : JSON.stringify(results[0].results.result.value),
                            'update' : update
                        });
                    results[1] = y.xmlToJson( y.query( queries[1] ).results );

                    return {
                        headers : {
                            "HTTP/1.1 status" : "201",
                            "Date" : new Date().getTime(),
                            "Location" : '/webdav/' + path,
                            "Content-Length" : contents.length,
                            "Connection" : "close",
                            "Content-Type" : "text/plain; charset=UTF-8"
                        }
                    };
                }();
            ]]></execute>
        </insert>
    </bindings>
</table>

Notes:

  • We could use some ruby like this in a rack app to intermediate between YQL and the WebDAV client:
        request = Rack::Request.new(env)
        
        contents = '';
        while part = request.body.read(8192)
          contents += part
        end
        
        query = "use '{the uri for the YQL table file using the code above}/put.xml' as table;"+
          "insert into table (path, contents) values ('#{file}', '#{contents}')"
        host = 'http://query.yahooapis.com'
        path = '/v1/public/yql'
        
        response = Net::HTTP.post_form( URI.parse( "#{host}#{path}" ), {
          'q' => query,
          'debug' => true
        } )
        doc = REXML::Document.new(response.body)
        headers = REXML::XPath.first( doc, "///headers" )
        # you could dump out the headers here
        # p(headers[0].text)
        
        # return an empty success response
        [204, {}, '']