From ff50190dbb06429fc35a993f89c3f84cb99109dd Mon Sep 17 00:00:00 2001 From: Cyrille Date: Tue, 21 Apr 2015 17:26:39 +0200 Subject: [PATCH 1/6] adding upload code - server side --- data/token.json | 1 + package.json | 10 +- routes/lmv.js | 287 +++++++++++++++++++++++++++++++++++++++++++++++ routes/upload.js | 141 +++++++++++++++++++++++ server.js | 2 + www/upload.html | 76 +++++++++++++ www/upload.js | 107 ++++++++++++++++++ 7 files changed, 623 insertions(+), 1 deletion(-) create mode 100644 data/token.json create mode 100644 routes/lmv.js create mode 100644 routes/upload.js create mode 100644 www/upload.html create mode 100644 www/upload.js diff --git a/data/token.json b/data/token.json new file mode 100644 index 0000000..02107cf --- /dev/null +++ b/data/token.json @@ -0,0 +1 @@ +{"token_type":"Bearer","expires_in":1799,"access_token":"85RXW0LXdMQM9W6jHT5AxqWGwQYv"} \ No newline at end of file diff --git a/package.json b/package.json index 01fb0f0..9aa3648 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,14 @@ "dependencies": { "express": "*", "request": "*", - "serve-favicon": "*" + "serve-favicon": "*", + "body-parser": ">= 1.11.0", + "formidable": ">= 1.0.17", + "fs": ">= 0.0.2", + "unirest": ">= 0.4.0", + "async": ">= 0.9.0", + "util": ">= 0.10.3", + "path": ">=0.11.14" + } } \ No newline at end of file diff --git a/routes/lmv.js b/routes/lmv.js new file mode 100644 index 0000000..261b715 --- /dev/null +++ b/routes/lmv.js @@ -0,0 +1,287 @@ +// +// Copyright (c) Autodesk, Inc. All rights reserved +// +// Node.js server workflow +// by Cyrille Fauvel - Autodesk Developer Network (ADN) +// January 2015 +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +var express =require ('express') ; +var request =require ('request') ; +// unirest (http://unirest.io/) or SuperAgent (http://visionmedia.github.io/superagent/) +var unirest =require('unirest') ; +var events =require('events') ; +var util =require ('util') ; +var path =require ('path') ; +var fs =require ('fs') ; +var credentials =require ('../credentials') ; + +// LMV object +function Lmv (bucketName) { + events.EventEmitter.call (this) ; + this.bucket =bucketName ; + this.creds =credentials ; +} +//Lmv.prototype.__proto__ =events.EventEmitter.prototype ; +util.inherits (Lmv, events.EventEmitter) ; + +/*static*/ Lmv.getToken =function () { + try { + var data =fs.readFileSync ('data/token.json') ; + var authResponse =JSON.parse (data) ; + return (authResponse.access_token) ; + } catch ( err ) { + console.log (err) ; + } + return ('') ; +} ; + +// POST /authentication/v1/authenticate +/*static*/ Lmv.refreshToken =function () { + console.log ('Refreshing Autodesk Service token') ; + var params ={ + client_id: credentials.ClientId, + client_secret: credentials.ClientSecret, + grant_type: 'client_credentials' + } ; + var endpoint =credentials.BaseUrl + '/authentication/v1/authenticate' ; + unirest.post (endpoint) + .header ('Accept', 'application/json') + .type ('application/x-www-form-urlencoded') + .send (params) + .end (function (response) { + try { + if ( response.statusCode != 200 ) + throw response ; + var authResponse =response.body ; + console.log ('Token: ' + JSON.stringify (authResponse)) ; + //authResponse.expires_at =Math.floor (Date.now () / 1000) + authResponse.expires_in ; + fs.writeFile ('data/token.json', JSON.stringify (authResponse), function (err) { + if ( err ) + throw err ; + }) ; + } catch ( err ) { + fs.unlinkSync ('data/token.json') ; + console.log ('Token: ERROR! (' + response.statusCode + ')') ; + } + }) + ; +} ; + +// GET /oss/v1/buckets/:bucket/details +Lmv.prototype.checkBucket =function () { + var self =this ; + unirest.get (self.creds.BaseUrl + '/oss/v1/buckets/' + self.bucket + '/details') + .header ('Accept', 'application/json') + .header ('Content-Type', 'application/json') + .header ('Authorization', 'Bearer ' + Lmv.getToken ()) + //.query (params) + .end (function (response) { + try { + if ( response.statusCode != 200 ) + throw response ; + self.emit ('success', response.raw_body) ; + } catch ( err ) { + self.emit ('fail', err) ; + } + }) + ; + return (this) ; +} ; + +// POST /oss/v1/buckets +Lmv.prototype.createBucket =function (policy) { + policy =policy || 'transient' ; + var self =this ; + unirest.post (self.creds.BaseUrl + '/oss/v1/buckets') + .header ('Accept', 'application/json') + .header ('Content-Type', 'application/json') + .header ('Authorization', 'Bearer ' + Lmv.getToken ()) + .send ({ 'bucketKey': self.bucket, 'policy': policy }) + .end (function (response) { + try { + if ( response.statusCode != 200 || !response.raw_body.hasOwnProperty ('key') ) + throw response ; + self.emit ('success', response.raw_body) ; + } catch ( err ) { + self.emit ('fail', err) ; + } + }) + ; + return (this) ; +} ; + +Lmv.prototype.createBucketIfNotExist =function (policy) { + policy =policy || 'transient' ; + var self =this ; + + unirest.get (self.creds.BaseUrl + '/oss/v1/buckets/' + self.bucket + '/details') + .header ('Accept', 'application/json') + .header ('Content-Type', 'application/json') + .header ('Authorization', 'Bearer ' + Lmv.getToken ()) + //.query (params) + .end (function (response) { + try { + if ( response.statusCode != 200 ) + throw response ; + try { + self.emit ('success', response.raw_body) ; + } catch ( err ) { + } + } catch ( err ) { + //- We need to create one if error == 404 (404 Not Found) + if ( Number.isInteger (err.statusCode) && err.statusCode == 404 ) { + unirest.post (self.creds.BaseUrl + '/oss/v1/buckets') + .header ('Accept', 'application/json') + .header ('Content-Type', 'application/json') + .header ('Authorization', 'Bearer ' + Lmv.getToken ()) + .send ({ 'bucketKey': self.bucket, 'policy': policy }) + .end (function (response) { + try { + if ( response.statusCode != 200 || !response.raw_body.hasOwnProperty ('key') ) + throw response ; + try { + self.emit ('success', response.raw_body) ; + } catch ( err ) { + } + } catch ( err ) { + self.emit ('fail', err) ; + } + }) + ; + } else { + self.emit ('fail', err) ; + } + } + }) + ; + return (this) ; +} ; + +// PUT /oss/v1/buckets/:bucket/objects/:filename +Lmv.prototype.uploadFile =function (filename) { + var self =this ; + var serverFile =path.normalize (__dirname + '/../' + filename) ; + var localFile =path.basename (filename) ; + + var file =fs.readFile (serverFile, function (err, data) { + if ( err ) + return (self.emit ('fail', err)) ; + + var endpoint ='/oss/v1/buckets/' + self.bucket + '/objects/' + localFile.replace (/ /g, '+') ; + unirest.put (self.creds.BaseUrl + endpoint) + .headers ({ + 'Accept': 'application/json', + 'Content-Type': 'application/octet-stream', + 'Authorization': ('Bearer ' + Lmv.getToken ()) + }) + .send (data) + .end (function (response) { + try { + if ( response.statusCode != 200 ) + throw response ; + try { + self.emit ('success', response.raw_body) ; + } catch ( err ) { + } + } catch ( err ) { + self.emit ('fail', err) ; + } + }) + ; + }) ; + return (this) ; +} ; + +// POST /viewingservice/v1/register +Lmv.prototype.register =function (urn) { + var self =this ; + var desc ={ 'urn': new Buffer (urn).toString ('base64') } ; + + unirest.post (self.creds.BaseUrl + '/viewingservice/v1/register') + .headers ({ + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': ('Bearer ' + Lmv.getToken ()) + }) + .send (desc) + .end (function (response) { + try { + if ( response.statusCode != 200 && response.statusCode != 201 ) + throw response ; + try { + self.emit ('success', { 'urn': desc.urn, 'response': response.body }) ; + } catch ( err ) { + } + } catch ( err ) { + self.emit ('fail', err) ; + } + }) + ; + return (this) ; +} ; + +Lmv.prototype.status =function (urn, params) { + var self =this ; + params =params || {} ; + + var endpoint ='/viewingservice/v1/' + urn + '/status' ; + unirest.get (self.creds.BaseUrl + endpoint) + .headers ({ + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': ('Bearer ' + Lmv.getToken ()) + }) + .query (params) + .end (function (response) { + try { + if ( response.statusCode != 200 ) + throw response ; + try { + self.emit ('success', response.body) ; + } catch ( err ) { + } + } catch ( err ) { + self.emit ('fail', err) ; + } + }) + ; + return (this) ; +} ; + +var router =express.Router () ; +router.Lmv =Lmv ; +module.exports =router ; + +// Utility +if ( !Number.isInteger ) { + Number.isInteger =function isInteger (nVal) { + return ( + typeof nVal === 'number' + && isFinite (nVal) + && nVal > -9007199254740992 + && nVal < 9007199254740992 + && Math.floor (nVal) === nVal + ) ; + } ; +} + +// Initialization +function initializeApp () { + var seconds =1700 ; // Service returns 1799 seconds bearer token + setInterval (Lmv.refreshToken, seconds * 1000) ; + Lmv.refreshToken () ; // and now! +} +initializeApp () ; diff --git a/routes/upload.js b/routes/upload.js new file mode 100644 index 0000000..1b8fe18 --- /dev/null +++ b/routes/upload.js @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Autodesk, Inc. All rights reserved +// Written by Cyrille Fauvel, 2015 - ADN/Developer Technical Services +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +/////////////////////////////////////////////////////////////////////////////// + +var express =require ('express') ; +var bodyParser =require ('body-parser') ; +var formidable = require('formidable') +var fs =require ('fs') ; +var async =require ('async') ; +var lmv =require ('./lmv.js') ; + +var router =express.Router () ; +router.use (bodyParser.json ()) ; + +router.post ('/file', function (req, res) { + /*req + .pipe (fs.createWriteStream ('data/' + req.headers ['x-file-name'])) + .on ('finish', function (err) { + res.json ({ 'name': req.headers ['x-file-name'] }) ; + }) + .on ('error', function (err) { + res.status (500).end () ; + }) + ;*/ + var filename ='' ; + + var form =new formidable.IncomingForm () ; + form.uploadDir ='data' ; + form + .on ('field', function (field, value) { + console.log (field, value) ; + }) + .on ('file', function (field, file) { + console.log (field, file) ; + fs.rename (file.path, form.uploadDir + '/' + file.name) ; + filename =file.name ; + }) + .on ('end', function () { + console.log ('-> upload done') ; + if ( filename == '' ) + res.status (500).end ('No file submitted!') ; + res.json ({ 'name': filename }) ; + }) + ; + form.parse(req); +}) ; + +router.post ('/translate', function (req, res) { + var filename ='data/' + req.body.name ; + var bucket = + 'model' + + new Date ().toISOString ().replace (/T/, '-').replace (/:+/g, '-').replace (/\..+/, '') + + '-' + lmv.Lmv.getToken ().toLowerCase ().replace (/\W+/g, '') ; + var policy ='transient' ; + + async.waterfall ([ + function (callbacks1) { + console.log ('createBucketIfNotExist') ; + new lmv.Lmv (bucket).createBucketIfNotExist (policy) + .on ('success', function (data) { + console.log ('Bucket already or now exist!') ; + callbacks1 (null, data) ; + }) + .on ('fail', function (err) { + console.log ('Failed to create bucket!') ; + callbacks1 (err) ; + }) + ; + }, + + function (arg1, callbacks2) { + console.log ('async upload') ; + new lmv.Lmv (bucket).uploadFile (filename) + .on ('success', function (data) { + console.log (filename + ' uploaded.') ; + callbacks2 (null, data) ; + }) + .on ('fail', function (err) { + console.log ('Failed to upload ' + filename + '!') ; + callbacks2 (err) ; + }) + ; + }, + + function (arg1, callbacks3) { + console.log ('Launching translation') ; + var urn =JSON.parse (arg1).objects [0].id ; + new lmv.Lmv (bucket).register (urn) + .on ('success', function (data) { + console.log ('Translation requested.') ; + callbacks3 (null, data) ; + }) + .on ('fail', function (err) { + console.log ('Failed to request translation!') ; + callbacks3 (err) ; + }) + ; + } + + ], function (err, results) { + if ( err != null ) { + if ( err.hasOwnProperty ('statusCode') && err.statusCode != 200 ) + return (res.status (err.statusCode).send (err.body.reason)) ; + if ( !err.raw_body.hasOwnProperty ('key') ) + return (res.status (500).send ('The server did not return a valid key')) ; + return (res.status (500).send ('An unknown error occurred!')) ; + } + + res.json (results) ; + }) ; + +}) ; + +router.get ('/translate/:urn/progress', function (req, res) { + var urn =req.params.urn ; + new lmv.Lmv ('').status (urn) + .on ('success', function (data) { + console.log (data.progress) ; + res.json (data) ; + }) + .on ('fail', function (err) { + res.status (404).end () ; + }) + ; +}) ; + +module.exports =router ; \ No newline at end of file diff --git a/server.js b/server.js index 989f4cf..ce315fd 100644 --- a/server.js +++ b/server.js @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////// var favicon = require('serve-favicon'); var api = require('./routes/api'); +var upload = require('./routes/upload'); var express = require('express'); var app = express(); @@ -24,6 +25,7 @@ var app = express(); app.use('/', express.static(__dirname + '/www')); app.use(favicon(__dirname + '/www/images/favicon.ico')); app.use('/api', api); +app.use('/api', upload); app.set('port', process.env.PORT || 3000); diff --git a/www/upload.html b/www/upload.html new file mode 100644 index 0000000..2e2bebc --- /dev/null +++ b/www/upload.html @@ -0,0 +1,76 @@ + + + + ADN Viewer Demo (client upload) + + + + + + + + + + + + + +
+
+
+

Upload and translate a file

+
+
+ +
+
+ Translate this one for me +
+ +
+
+
+
+
+ +
+
+
+

My URNs

+
+
+
+
+ +
+ +
+ +
+ My URN list +
Click on a urn below to launch the viewer
+
+
+
+
+ + + diff --git a/www/upload.js b/www/upload.js new file mode 100644 index 0000000..9ef262f --- /dev/null +++ b/www/upload.js @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Autodesk, Inc. All rights reserved +// Written by Cyrille Fauvel, 2015 - ADN/Developer Technical Services +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +///////////////////////////////////////////////////////////////////////////////// + +$(document).ready (function () { + + $('#btnTranslateThisOne').click (function (evt) { + var files =document.getElementById ('files').files ; + if ( files.length == 0 ) + return ; + + $.each (files, function (key, value) { + var data =new FormData () ; + data.append (key, value) ; + + $.ajax ({ + url: 'http://' + window.location.host + '/api/file', + type: 'post', + headers: { 'x-file-name': value.name }, + data: data, + cache: false, + //dataType: 'json', + processData: false, // Don't process the files + contentType: false, // Set content type to false as jQuery will tell the server its a query string request + complete: null + }).done (function (data) { + $('#msg').text (value.name + ' file uploaded on your server') ; + translate (data) ; + }).fail (function (xhr, ajaxOptions, thrownError) { + $('#msg').text (value.name + ' upload failed!') ; + }) ; + }) ; + + }) ; + + $('#btnAddThisOne').click (function (evt) { + var urn =$('#urn').val ().trim () ; + if ( urn == '' ) + return ; + AddThisOne (urn) ; + }) ; + +}) ; + +function AddThisOne (urn) { + var id =urn.replace (/=+/g, '') ; + $('#list').append ('
' + + '' + + '
' + ) ; + $('#' + id).click (function (evt) { + window.open ('/?urn=' + $(this).text (), '_blank') ; + }) ; +} + +function translate (data) { + $('#msg').text (data.name + ' translation request...') ; + $.ajax ({ + url: '/api/translate', + type: 'post', + data: JSON.stringify (data), + timeout: 0, + contentType: 'application/json', + complete: null + }).done (function (response) { + $('#msg').text (data.name + ' translation requested...') ; + setTimeout (function () { translateProgress (response.urn) ; }, 5000) ; + }).fail (function (xhr, ajaxOptions, thrownError) { + $('#msg').text (data.name + ' translation request failed!') ; + }) ; +} + +function translateProgress (urn) { + $.ajax ({ + url: '/api/translate/' + urn + '/progress', + type: 'get', + data: null, + contentType: 'application/json', + complete: null + }).done (function (response) { + if ( response.progress == 'complete' ) { + AddThisOne (response.urn) ; + $('#msg').text ('') ; + } else { + var name =window.atob (urn) ; + var filename =name.replace (/^.*[\\\/]/, '') ; + $('#msg').text (filename + ': ' + response.progress) ; + setTimeout (function () { translateProgress (urn) ; }, 500) ; + } + }).fail (function (xhr, ajaxOptions, thrownError) { + $('#msg').text ('Progress request failed!') ; + }) ; +} \ No newline at end of file From 986825e2fc3954bfe04ff503762945d7c64f5b0a Mon Sep 17 00:00:00 2001 From: Cyrille Date: Wed, 13 May 2015 14:39:49 +0200 Subject: [PATCH 2/6] typos and system variables --- .gitignore | 6 +++++- README-stg.md | 8 ++++---- README.md | 8 ++++---- credentials_.js | 4 ++-- data/token.json | 2 +- package.json | 45 +++++++++++++++++++++++++++++++-------------- routes/api.js | 17 +++++++++++------ 7 files changed, 58 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index c2ad1db..50d8c4b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,8 @@ node_modules credentials.js # webstorm project files -.idea \ No newline at end of file +.idea + +.DS_store +Thumbs.db +*.bak diff --git a/README-stg.md b/README-stg.md index 56bacc1..04eafa6 100644 --- a/README-stg.md +++ b/README-stg.md @@ -43,11 +43,11 @@ get model URNs - as explained in the Setup/Usage Instructions. ``` cp credentials_.js credentials.js ``` -* Replace the placeholder with your own keys in credentials.js, line #23 and #24
+* Replace the placeholders with your own keys in credentials.js, line #23 and #24
``` - credentials.ClientId = ''; + credentials.ConsumerKey =process.env.CONSUMERKEY || ''; - credentials.ClientSecret = ''; + credentials.ConsumerSecret =process.env.CONSUMERSECRET || ''; ``` * In file credentials.js line #26, replace the BaseUrl address by the staging server address
``` @@ -56,7 +56,7 @@ get model URNs - as explained in the Setup/Usage Instructions. * Upload one of your models to your account and get its URN using another workflow sample, for example, - [this workflow sample in .Net WPF application](https://github.com/Developer-Autodesk/workflow-wpf-view.and.data.api) if you are using windows - or [this workflow sample in Mac OS Swift](https://github.com/Developer-Autodesk/workflow-macos-swift-view.and.data.api) if you are using Mac - - or this [WEB page](http://javalmvwalkthrough-vq2mmximxb.elasticbeanstalk.com/) + - or this [WEB page](http://models.autodesk.io/) or this [one](http://javalmvwalkthrough-vq2mmximxb.elasticbeanstalk.com/) * Copy the URN which was generated in the previous step in file /www/index.js at line #18
``` var defaultUrn = ''; diff --git a/README.md b/README.md index 890eed8..ca29af6 100644 --- a/README.md +++ b/README.md @@ -34,16 +34,16 @@ get model URNs - as explained in the Setup/Usage Instructions. ``` cp credentials_.js credentials.js ``` -* Replace the placeholder with your own keys in credentials.js, line #23 and #24
+* Replace the placeholders with your own keys in credentials.js, line #23 and #24
``` - credentials.ClientId = ''; + credentials.ConsumerKey =process.env.CONSUMERKEY || ''; - credentials.ClientSecret = ''; + credentials.ConsumerSecret =process.env.CONSUMERSECRET || ''; ``` * Upload one of your models to your account and get its URN using another workflow sample, for example, - [this workflow sample in .Net WPF application](https://github.com/Developer-Autodesk/workflow-wpf-view.and.data.api) if you are using windows - or [this workflow sample in Mac OS Swift](https://github.com/Developer-Autodesk/workflow-macos-swift-view.and.data.api) if you are using Mac - - or this [WEB page](http://javalmvwalkthrough-vq2mmximxb.elasticbeanstalk.com/) + - or this [WEB page](http://models.autodesk.io/) or this [one](http://javalmvwalkthrough-vq2mmximxb.elasticbeanstalk.com/) * Copy the URN which was generated in the previous step in file /www/index.js at line #18
``` var defaultUrn = ''; diff --git a/credentials_.js b/credentials_.js index 7faa047..eefabf9 100644 --- a/credentials_.js +++ b/credentials_.js @@ -20,8 +20,8 @@ var credentials ={} ; // Replace placeholder below by the Consumer Key and Consumer Secret you got from // http://developer.autodesk.com/ for the production server -credentials.ClientId ='' ; -credentials.ClientSecret ='' ; +credentials.ConsumerKey =process.env.CONSUMERKEY || '' ; +credentials.ConsumerSecret =process.env.CONSUMERSECRET || '' ; // If you which to use the Autodesk View & Data API on the staging server, change this url credentials.BaseUrl = 'https://developer.api.autodesk.com' ; diff --git a/data/token.json b/data/token.json index 02107cf..d701a3f 100644 --- a/data/token.json +++ b/data/token.json @@ -1 +1 @@ -{"token_type":"Bearer","expires_in":1799,"access_token":"85RXW0LXdMQM9W6jHT5AxqWGwQYv"} \ No newline at end of file +{"token_type":"Bearer","expires_in":1799,"access_token":"ohS9SxMCK3EoMj4psHNcQyMt1YBy"} \ No newline at end of file diff --git a/package.json b/package.json index 9aa3648..0df0a10 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,36 @@ { "name": "AdnViewerBasic", - - "version": "0.0.0", - + "description": "A basic node.js server sample", + "version": "1.0.0", "dependencies": { - "express": "*", - "request": "*", - "serve-favicon": "*", - "body-parser": ">= 1.11.0", - "formidable": ">= 1.0.17", - "fs": ">= 0.0.2", - "unirest": ">= 0.4.0", - "async": ">= 0.9.0", - "util": ">= 0.10.3", - "path": ">=0.11.14" - + "serve-favicon": ">= 2.2.0", + "express": ">= 4.12.3", + "request": ">= 2.55.0", + "body-parser": ">= 1.11.0", + "formidable": ">= 1.0.17", + "fs": ">= 0.0.2", + "unirest": ">= 0.4.0", + "async": ">= 0.9.0", + "util": ">= 0.10.3", + "path": ">=0.11.14" + }, + "files": [ + "LICENSE", + "README.md" + ], + "engines": { + "node": ">= 0.10.0" + }, + "contributors": [ + "Philippe Leefsma " + ], + "license": "MIT", + "scripts": { + "start": "node server.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/Developer-Autodesk/workflow-node.js-view.and.data.api.git" + } } } \ No newline at end of file diff --git a/routes/api.js b/routes/api.js index 408f063..cd6ca71 100644 --- a/routes/api.js +++ b/routes/api.js @@ -15,8 +15,14 @@ // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. ///////////////////////////////////////////////////////////////////////////////// -var credentials = require('../credentials'); - +var fs = require('fs'); +var credentials ; +if ( !fs.existsSync ('credentials.js') ) { + console.log ('No credentials.js file present, assuming using CONSUMERKEY & CONSUMERSECRET system variables.') ; + credentials =require('../credentials_') ; +} else { + credentials =require('../credentials') ; +} var express = require('express'); var request = require('request'); @@ -27,15 +33,14 @@ var router = express.Router(); /////////////////////////////////////////////////////////////////////////////// router.get('/token', function (req, res) { var params = { - client_id: credentials.ClientId, - client_secret: credentials.ClientSecret, + client_id: credentials.ConsumerKey, + client_secret: credentials.ConsumerSecret, grant_type: 'client_credentials' - } + } ; request.post( credentials.BaseUrl + '/authentication/v1/authenticate', { form: params }, - function (error, response, body) { if (!error && response.statusCode == 200) { res.send(body); From d1ebf255834ec5093bdf431cb6639ae579009650 Mon Sep 17 00:00:00 2001 From: Cyrille Date: Sat, 23 May 2015 13:18:35 +0200 Subject: [PATCH 3/6] using std naming convention --- README-stg.md | 4 +-- README.md | 4 +-- credentials_.js | 22 ++++++++++------ data/.gitignore | 2 ++ data/token.json | 1 - package.json | 67 +++++++++++++++++++++++-------------------------- routes/api.js | 42 +++++++++++-------------------- routes/lmv.js | 14 ++++------- 8 files changed, 72 insertions(+), 84 deletions(-) create mode 100644 data/.gitignore delete mode 100644 data/token.json diff --git a/README-stg.md b/README-stg.md index 04eafa6..15cc34d 100644 --- a/README-stg.md +++ b/README-stg.md @@ -45,9 +45,9 @@ get model URNs - as explained in the Setup/Usage Instructions. ``` * Replace the placeholders with your own keys in credentials.js, line #23 and #24
``` - credentials.ConsumerKey =process.env.CONSUMERKEY || ''; + client_id: process.env.CONSUMERKEY || ''; - credentials.ConsumerSecret =process.env.CONSUMERSECRET || ''; + client_secret: process.env.CONSUMERSECRET || ''; ``` * In file credentials.js line #26, replace the BaseUrl address by the staging server address
``` diff --git a/README.md b/README.md index ca29af6..7341c82 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ get model URNs - as explained in the Setup/Usage Instructions. ``` * Replace the placeholders with your own keys in credentials.js, line #23 and #24
``` - credentials.ConsumerKey =process.env.CONSUMERKEY || ''; + client_id: process.env.CONSUMERKEY || ''; - credentials.ConsumerSecret =process.env.CONSUMERSECRET || ''; + client_secret: process.env.CONSUMERSECRET || ''; ``` * Upload one of your models to your account and get its URN using another workflow sample, for example, - [this workflow sample in .Net WPF application](https://github.com/Developer-Autodesk/workflow-wpf-view.and.data.api) if you are using windows diff --git a/credentials_.js b/credentials_.js index eefabf9..fac0c4f 100644 --- a/credentials_.js +++ b/credentials_.js @@ -15,15 +15,21 @@ // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. ///////////////////////////////////////////////////////////////////////////////// +var credentials ={ -var credentials ={} ; + credentials: { + // Replace placeholder below by the Consumer Key and Consumer Secret you got from + // http://developer.autodesk.com/ for the production server + client_id: process.env.CONSUMERKEY || '', + client_secret: process.env.CONSUMERSECRET || '', + grant_type: 'client_credentials' + }, + + // If you which to use the Autodesk View & Data API on the staging server, change this url + BaseUrl: 'https://developer.api.autodesk.com', + Version: 'v1' +} ; -// Replace placeholder below by the Consumer Key and Consumer Secret you got from -// http://developer.autodesk.com/ for the production server -credentials.ConsumerKey =process.env.CONSUMERKEY || '' ; -credentials.ConsumerSecret =process.env.CONSUMERSECRET || '' ; - -// If you which to use the Autodesk View & Data API on the staging server, change this url -credentials.BaseUrl = 'https://developer.api.autodesk.com' ; +credentials.Authentication =credentials.BaseUrl + '/authentication/' + credentials.Version + '/authenticate' module.exports =credentials ; diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/data/token.json b/data/token.json deleted file mode 100644 index d701a3f..0000000 --- a/data/token.json +++ /dev/null @@ -1 +0,0 @@ -{"token_type":"Bearer","expires_in":1799,"access_token":"ohS9SxMCK3EoMj4psHNcQyMt1YBy"} \ No newline at end of file diff --git a/package.json b/package.json index 0df0a10..89b8c26 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,33 @@ { - "name": "AdnViewerBasic", - "description": "A basic node.js server sample", - "version": "1.0.0", - "dependencies": { - "serve-favicon": ">= 2.2.0", - "express": ">= 4.12.3", - "request": ">= 2.55.0", - "body-parser": ">= 1.11.0", - "formidable": ">= 1.0.17", - "fs": ">= 0.0.2", - "unirest": ">= 0.4.0", - "async": ">= 0.9.0", - "util": ">= 0.10.3", - "path": ">=0.11.14" - }, - "files": [ - "LICENSE", - "README.md" - ], - "engines": { - "node": ">= 0.10.0" - }, - "contributors": [ - "Philippe Leefsma " - ], - "license": "MIT", - "scripts": { - "start": "node server.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/Developer-Autodesk/workflow-node.js-view.and.data.api.git" - } - } -} \ No newline at end of file + "name": "AdnViewerBasic", + "description": "A basic node.js server sample", + "version": "1.0.0", + "dependencies": { + "serve-favicon": ">= 2.2.0", + "express": ">= 4.12.3", + "request": ">= 2.55.0", + "body-parser": ">= 1.11.0", + "formidable": ">= 1.0.17", + "unirest": ">= 0.4.0", + "async": ">= 0.9.0" + }, + "files": [ + "LICENSE", + "README.md" + ], + "engines": { + "node": ">= 0.10.0" + }, + "contributors": [ + "Philippe Leefsma ", + "Cyrille Fauvel " + ], + "license": "MIT", + "scripts": { + "start": "node server.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/Developer-Autodesk/workflow-node.js-view.and.data.api.git" + } +} diff --git a/routes/api.js b/routes/api.js index cd6ca71..7aaa419 100644 --- a/routes/api.js +++ b/routes/api.js @@ -15,37 +15,25 @@ // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. ///////////////////////////////////////////////////////////////////////////////// -var fs = require('fs'); -var credentials ; -if ( !fs.existsSync ('credentials.js') ) { - console.log ('No credentials.js file present, assuming using CONSUMERKEY & CONSUMERSECRET system variables.') ; - credentials =require('../credentials_') ; -} else { - credentials =require('../credentials') ; -} -var express = require('express'); -var request = require('request'); +var credentials =(require ('fs').existsSync ('credentials.js') ? + require('../credentials') + : (console.log ('No credentials.js file present, assuming using CONSUMERKEY & CONSUMERSECRET system variables.'), require('../credentials_'))) ; +var express =require ('express') ; +var request =require ('request') ; -var router = express.Router(); +var router =express.Router () ; /////////////////////////////////////////////////////////////////////////////// // Generates access token /////////////////////////////////////////////////////////////////////////////// -router.get('/token', function (req, res) { - var params = { - client_id: credentials.ConsumerKey, - client_secret: credentials.ConsumerSecret, - grant_type: 'client_credentials' - } ; - - request.post( - credentials.BaseUrl + '/authentication/v1/authenticate', - { form: params }, +router.get ('/token', function (req, res) { + request.post ( + credentials.Authentication, + { form: credentials.credentials }, function (error, response, body) { - if (!error && response.statusCode == 200) { - res.send(body); - } - }); -}); + if ( !error && response.statusCode == 200 ) + res.send (body) ; + }) ; +}) ; -module.exports = router; +module.exports =router ; diff --git a/routes/lmv.js b/routes/lmv.js index 261b715..a2200b9 100644 --- a/routes/lmv.js +++ b/routes/lmv.js @@ -18,6 +18,9 @@ // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // +var credentials =(require ('fs').existsSync ('credentials.js') ? + require('../credentials') + : (console.log ('No credentials.js file present, assuming using CONSUMERKEY & CONSUMERSECRET system variables.'), require('../credentials_'))) ; var express =require ('express') ; var request =require ('request') ; // unirest (http://unirest.io/) or SuperAgent (http://visionmedia.github.io/superagent/) @@ -26,7 +29,6 @@ var events =require('events') ; var util =require ('util') ; var path =require ('path') ; var fs =require ('fs') ; -var credentials =require ('../credentials') ; // LMV object function Lmv (bucketName) { @@ -51,16 +53,10 @@ util.inherits (Lmv, events.EventEmitter) ; // POST /authentication/v1/authenticate /*static*/ Lmv.refreshToken =function () { console.log ('Refreshing Autodesk Service token') ; - var params ={ - client_id: credentials.ClientId, - client_secret: credentials.ClientSecret, - grant_type: 'client_credentials' - } ; - var endpoint =credentials.BaseUrl + '/authentication/v1/authenticate' ; - unirest.post (endpoint) + unirest.post (credentials.Authentication) .header ('Accept', 'application/json') .type ('application/x-www-form-urlencoded') - .send (params) + .send (credentials.credentials) .end (function (response) { try { if ( response.statusCode != 200 ) From b71e785824f9bb09aa3962ad04752f992a2672cd Mon Sep 17 00:00:00 2001 From: cyrillef Date: Thu, 28 May 2015 19:27:02 +0200 Subject: [PATCH 4/6] bugfix --- routes/lmv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/lmv.js b/routes/lmv.js index a2200b9..e639c29 100644 --- a/routes/lmv.js +++ b/routes/lmv.js @@ -55,7 +55,7 @@ util.inherits (Lmv, events.EventEmitter) ; console.log ('Refreshing Autodesk Service token') ; unirest.post (credentials.Authentication) .header ('Accept', 'application/json') - .type ('application/x-www-form-urlencoded') + //.type ('application/x-www-form-urlencoded') .send (credentials.credentials) .end (function (response) { try { From 5f4866764d08e26ff77790a406f5e61db5d1924b Mon Sep 17 00:00:00 2001 From: Cyrille Date: Thu, 28 May 2015 19:34:10 +0200 Subject: [PATCH 5/6] Delete Thumbs.db --- www/images/Thumbs.db | Bin 32768 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 www/images/Thumbs.db diff --git a/www/images/Thumbs.db b/www/images/Thumbs.db deleted file mode 100644 index 02d42c84c47625b39a521d7ae831a558f4587492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 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 Date: Wed, 17 Jun 2015 16:06:43 +0200 Subject: [PATCH 6/6] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7341c82..5afcb32 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ get model URNs - as explained in the Setup/Usage Instructions. ``` * Replace the placeholders with your own keys in credentials.js, line #23 and #24
``` - client_id: process.env.CONSUMERKEY || ''; + client_id: process.env.CONSUMERKEY || '', - client_secret: process.env.CONSUMERSECRET || ''; + client_secret: process.env.CONSUMERSECRET || '', ``` * Upload one of your models to your account and get its URN using another workflow sample, for example, - [this workflow sample in .Net WPF application](https://github.com/Developer-Autodesk/workflow-wpf-view.and.data.api) if you are using windows