/
0050_door_data_service.html
321 lines (305 loc) · 19 KB
/
0050_door_data_service.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
<p><head>
<title>The 3D Web Coder</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="3dwc.css"/>
<script src="run_prettify.js" type="text/javascript"></script>
<!--
<script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js" type="text/javascript"></script>
-->
</head></p>
<!---
#mongolab #heroku #mongoosejs #expressjs
#Autodesk #IoT #SeeControl #cloud #adsk
#3dwebcoder #python #adskdevnetwrk #adsk #markdown #asciidoc
#gcal #caldav #cloud #googleapi #restapi
#milanojs
#3dwebaccel #prague #webgl #3dweb #a360
#au2015 #autocad #inventor #ah8 #cubeathens #developers
#aws #revitapi #jquery #handlebars #heroku
#ViewAndDataAPI
#JsFiddle #Reactjs #3dwebcoder #autodesku #rtceur
akn_include
#RestSharp
C# DoorData and Node.js DoorService Classes #Mongodb #Mongoose #3dwebcoder #revitapi #restapi #javascript #nodejs #adskdevnetwrk #adsk #bim #aec
I continued improving my REST API node.js web server, mongo database and C# REST API client, and also set up a Google domain rerouting
– CompHound domain, organisation web site and rerouting
– Implement a DoorData container class
– Updated Get to return a list of deserialised DoorData instances
– Pass a DoorData instance to the Put method
– Implement a REST API DoorService class...
-->
<h3>C# DoorData and Node.js DoorService Classes</h3>
<p>I continued implementing the numerous improvements suggested by
<a href="http://adndevblog.typepad.com/autocad/philippe-leefsma.html">Philippe Leefsma</a> to
my REST API node.js web server, mongo database and C# REST API client:</p>
<ul>
<li><a href="#2">CompHound domain, organisation web site and rerouting</a></li>
<li><a href="#3">Implement a DoorData container class</a></li>
<li><a href="#4">Updated Get to return a list of deserialised DoorData instances</a></li>
<li><a href="#5">Pass a DoorData instance to the Put method</a></li>
<li><a href="#6">Implement a REST API DoorService class</a></li>
</ul>
<p>This finally completes the list improvements to the
<a href="https://github.com/jeremytammik/firerating">FireRating in the cloud</a> sample:</p>
<ul>
<li>Implement a door data container object</li>
<li>Convert GET and PUT to use arrays of door objects</li>
<li>Use the RestSharp templated object transfer and deserialiser</li>
<li>Set up the web server to use the <code>{upsert:true}</code> option</li>
</ul>
<p>After I finish writing this, I can switch back and continue work on the new
<a href="http://the3dwebcoder.typepad.com/blog/2015/09/comphound-restsharp-mongoose-put-and-post.html#2">CompHound component tracker</a> in
preparation for my upcoming conference presentations at
<a href="http://www.rtcevents.com/rtc2015eu">RTC Europe</a> in Budapest end of October and
<a href="http://au.autodesk.com">Autodesk University</a> in Las Vegas in December.</p>
<h4><a name="2"></a>CompHound Domain, Organisation Web Site and Rerouting</h4>
<p>By the way, I already reserved the
<a href="http://comphound.net">comphound.net</a> domain and set up a skeleton web site for the
<a href="https://github.com/comphound">CompHound organisation</a>, with a lot of help from my colleague
<a href="http://through-the-interface.typepad.com/through_the_interface/about-the-author.html">Kean Walmsley</a>.</p>
<p>By the way, I am happy to say that Kean is here with us this week at the
<a href="http://autodeskcloudaccelerator.com/prague">Prague Autodesk Cloud Accelerator</a>,
mainly providing support for the
<a href="http://autocad.io">AutoCAD I/O web service</a>, e.g., demonstrating his
<a href="http://through-the-interface.typepad.com/through_the_interface/2015/08/lets-get-physical-laser-cutting-the-output-from-jigsawifycom.html">Jigsawify app</a>.</p>
<p>Anyway, following Kean's lead, I implemented a normal GitHub Pages web site for the CompHound organisation, accessible through
<a href="https://comphound.github.io">comphound.github.io</a>.</p>
<p>I paid $12 to buy the top-level domain name <code>comphound.net</code> from
<a href="http://domains.google.com">Google domains</a> and set up the domain properties to route the <code>comphound.net</code> domain to the GitHub Pages web site:</p>
<p><center>
<img src="img/comphound_net_google_domain.png" alt="Comphound Google domain properties" width="600"/>
</center></p>
<h4><a name="3"></a>Implement a DoorData Container Class</h4>
<p>Back to the firerating sample, I continued the seemingly never-ending process of perfectioning the
<a href="https://github.com/jeremytammik/firerating">firerating mongo database node.js web server</a> and its
<a href="https://github.com/jeremytammik/FireRatingCloud">FireRatingCloud C# REST client</a>.</p>
<p>I'm getting there, though!</p>
<p>The steps today define an explicit helper class for RestSharp to serialise and deserialise the door data between the C# client and the REST API calls:</p>
<pre class="code">
<span class="blue">class</span> <span class="teal">DoorData</span>
{
<span class="blue">public</span> <span class="blue">string</span> _id { <span class="blue">get</span>; <span class="blue">set</span>; }
<span class="blue">public</span> <span class="blue">string</span> project_id { <span class="blue">get</span>; <span class="blue">set</span>; }
<span class="blue">public</span> <span class="blue">string</span> level { <span class="blue">get</span>; <span class="blue">set</span>; }
<span class="blue">public</span> <span class="blue">string</span> tag { <span class="blue">get</span>; <span class="blue">set</span>; }
<span class="blue">public</span> <span class="blue">double</span> firerating { <span class="blue">get</span>; <span class="blue">set</span>; }
<span class="gray">///</span><span class="green"> </span><span class="gray"><summary></span>
<span class="gray">///</span><span class="green"> Constructor to populate instance by </span>
<span class="gray">///</span><span class="green"> deserialising the REST GET response.</span>
<span class="gray">///</span><span class="green"> </span><span class="gray"></summary></span>
<span class="blue">public</span> DoorData()
{
}
<span class="gray">///</span><span class="green"> </span><span class="gray"><summary></span>
<span class="gray">///</span><span class="green"> Constructor from BIM to serialise for</span>
<span class="gray">///</span><span class="green"> the REST POST or PUT request.</span>
<span class="gray">///</span><span class="green"> </span><span class="gray"></summary></span>
<span class="gray">///</span><span class="green"> </span><span class="gray"><param name="door"></param></span>
<span class="gray">///</span><span class="green"> </span><span class="gray"><param name="project_id"></param></span>
<span class="gray">///</span><span class="green"> </span><span class="gray"><param name="paramGuid"></param></span>
<span class="blue">public</span> DoorData(
<span class="teal">Element</span> door,
<span class="blue">string</span> project_id_arg,
<span class="teal">Guid</span> paramGuid )
{
<span class="teal">Document</span> doc = door.Document;
_id = door.UniqueId;
project_id = project_id_arg;
level = doc.GetElement( door.LevelId ).Name;
tag = door.get_Parameter(
<span class="teal">BuiltInParameter</span>.ALL_MODEL_MARK ).AsString();
firerating = door.get_Parameter( paramGuid )
.AsDouble();
}
}
</pre>
<h4><a name="4"></a>Updated Get to Return a List of Deserialised DoorData Instances</h4>
<p>With the DoorData class in place, I updated the RestSharp REST API GET method to return a list of deserialised DoorData instances like this:</p>
<pre class="code">
<span class="gray">///</span><span class="green"> </span><span class="gray"><summary></span>
<span class="gray">///</span><span class="green"> GET JSON document data from </span>
<span class="gray">///</span><span class="green"> the specified mongoDB collection.</span>
<span class="gray">///</span><span class="green"> </span><span class="gray"></summary></span>
<span class="blue">public</span> <span class="blue">static</span> <span class="teal">List</span><<span class="teal">DoorData</span>> Get(
<span class="blue">string</span> collection_name_and_id )
{
<span class="blue">var</span> client = <span class="blue">new</span> <span class="teal">RestClient</span>( RestApiBaseUrl );
<span class="blue">var</span> request = <span class="blue">new</span> <span class="teal">RestRequest</span>( _api_version + <span class="maroon">"/"</span>
+ collection_name_and_id, <span class="teal">Method</span>.GET );
<span class="teal">IRestResponse</span><<span class="teal">List</span><<span class="teal">DoorData</span>>> response
= client.Execute<<span class="teal">List</span><<span class="teal">DoorData</span>>>( request );
<span class="blue">return</span> response.Data;
}
</pre>
<p>It's getting shorter and simpler all the time, isn't it?</p>
<p>The mainline call to make use of this is shorter, simpler and more maintainable too:</p>
<pre class="code">
<span class="green">// Determine custom project identifier.</span>
<span class="blue">string</span> project_id = <span class="teal">Util</span>.GetProjectIdentifier( doc );
<span class="green">// Get all doors referencing this project.</span>
<span class="blue">string</span> query = <span class="maroon">"doors/project/"</span> + project_id;
<span class="teal">List</span><<span class="teal">DoorData</span>> doors = <span class="teal">Util</span>.Get( query );
<span class="blue">if</span>( <span class="blue">null</span> != doors && 0 < doors.Count )
{
<span class="blue">using</span>( <span class="teal">Transaction</span> t = <span class="blue">new</span> <span class="teal">Transaction</span>( doc ) )
{
t.Start( <span class="maroon">"Import Fire Rating Values"</span> );
<span class="green">// Retrieve element unique id and </span>
<span class="green">// FireRating parameter values.</span>
<span class="blue">foreach</span>( <span class="teal">DoorData</span> d <span class="blue">in</span> doors )
{
<span class="blue">string</span> uid = d._id;
<span class="teal">Element</span> e = doc.GetElement( uid );
<span class="blue">if</span>( <span class="blue">null</span> == e )
{
message = <span class="blue">string</span>.Format(
<span class="maroon">"Error retrieving element for "</span>
+ <span class="maroon">"unique id {0}."</span>, uid );
<span class="blue">return</span> <span class="teal">Result</span>.Failed;
}
<span class="teal">Parameter</span> p = e.get_Parameter( paramGuid );
<span class="blue">if</span>( <span class="blue">null</span> == p )
{
message = <span class="blue">string</span>.Format(
<span class="maroon">"Error retrieving shared parameter on "</span>
+ <span class="maroon">" element with unique id {0}."</span>, uid );
<span class="blue">return</span> <span class="teal">Result</span>.Failed;
}
<span class="blue">object</span> fire_rating = d.firerating;
p.Set( (<span class="blue">double</span>) fire_rating );
}
t.Commit();
}
}
</pre>
<p>These enhancements are captured in
<a href="https://github.com/jeremytammik/FireRatingCloud/releases/tag/2016.0.0.11">FireRatingCloud release 2016.0.0.11</a>.</p>
<p>Now that the RestSharp JSON deserialiser is generating the C# DoorData instances, I can remove the JsonParser.cs module that was previously needed to achieve this.</p>
<h4><a name="5"></a>Pass a DoorData instance to the Put method</h4>
<p>With that in place, let's clean up the PUT call and pass in a DoorData instance to that as well, instead of a generic object:</p>
<pre class="code">
<span class="gray">///</span><span class="green"> </span><span class="gray"><summary></span>
<span class="gray">///</span><span class="green"> PUT JSON document data into </span>
<span class="gray">///</span><span class="green"> the specified mongoDB collection.</span>
<span class="gray">///</span><span class="green"> </span><span class="gray"></summary></span>
<span class="blue">public</span> <span class="blue">static</span> <span class="blue">string</span> Put(
<span class="blue">string</span> collection_name_and_id,
<span class="teal">DoorData</span> doorData )
{
<span class="blue">var</span> client = <span class="blue">new</span> <span class="teal">RestClient</span>( RestApiBaseUrl );
<span class="blue">var</span> request = <span class="blue">new</span> <span class="teal">RestRequest</span>( _api_version + <span class="maroon">"/"</span>
+ collection_name_and_id, <span class="teal">Method</span>.PUT );
request.RequestFormat = <span class="teal">DataFormat</span>.Json;
request.AddBody( doorData ); <span class="green">// uses JsonSerializer</span>
<span class="teal">IRestResponse</span> response = client.Execute( request );
<span class="blue">var</span> content = response.Content; <span class="green">// raw content as string</span>
<span class="blue">return</span> content;
}
</pre>
<p>Again, it is now shorter and simpler than before, and the same applies to the mainline call:</p>
<pre class="code">
<span class="green">// Loop through all elements of the given target</span>
<span class="green">// category and export the shared parameter value </span>
<span class="green">// specified by paramGuid for each.</span>
<span class="teal">FilteredElementCollector</span> collector
= <span class="teal">Util</span>.GetTargetInstances( doc,
<span class="teal">Cmd_1_CreateAndBindSharedParameter</span>.Target );
<span class="blue">int</span> n = collector.Count<<span class="teal">Element</span>>();
<span class="teal">DoorData</span> doorData;
<span class="blue">string</span> jsonResponse;
<span class="blue">foreach</span>( <span class="teal">Element</span> e <span class="blue">in</span> collector )
{
<span class="teal">Debug</span>.Print( e.Id.IntegerValue.ToString() );
doorData = <span class="blue">new</span> <span class="teal">DoorData</span>( e,
project_id, paramGuid );
jsonResponse = <span class="teal">Util</span>.Put(
<span class="maroon">"doors/"</span> + e.UniqueId, doorData );
<span class="teal">Debug</span>.Print( jsonResponse );
}
</pre>
<p>This update was captured in <a href="https://github.com/jeremytammik/FireRatingCloud/releases/tag/2016.0.0.12">FireRatingCloud release 2016.0.0.12</a>
– commented out JsonParser code and pass DoorData instance to Put method.</p>
<p>With that, we are finally all done cleaning up the C# REST API client, as far as I know.</p>
<h4><a name="6"></a>Implement a REST API DoorService Class Instead of Individual Separate <code>module.exports</code> Functions</h4>
<p>With the C# REST API client utterly perfected, there are one little hiccup to iron out in the node.js web server.</p>
<p>Philippe suggests:</p>
<blockquote>
<p>About the web project: I would rename instances.js into <code>instanceService</code> for example; that's how such a component is generally called. I would also write it as an object with methods and export the whole object rather than <code>export.eachMethod = ...</code> as you do know.</p>
</blockquote>
<p>The REST API functionality that he is referring to is implemented in the two JavaScript modules
<a href="https://github.com/jeremytammik/firerating/blob/master/routes.js">routes.js</a> and
<a href="https://github.com/jeremytammik/firerating/blob/master/controller/doors_v1.js">doors_v1.js</a>.</p>
<p>As suggested by Philippe, I modified the latter to define an explicit JavaScript object encapsulating all the function definitions and export that:</p>
<pre class="prettyprint">
DoorService = {
findAll : function(req, res){
Door.find({},function(err, results) {
return res.send(results);
});
},
. . .
};
module.exports = DoorService;
</pre>
<p>On my first attempt, this did not work.
Probably I forgot to add the <code>module.</code> prefix in front of <code>exports</code>.</p>
<p>I fixed that after reading the illuminating little discussion on
<a href="http://openmymind.net/2012/2/3/Node-Require-and-Exports">node.js, require and exports</a> by Karl Seguin.</p>
<p>Now the router can simply import the DoorService object and route to its methods one by one like this:</p>
<pre class="prettyprint">
module.exports = function(app) {
var DoorService = require('./controller/doors_v1');
app.get('/api/v1/doors', DoorService.findAll);
app.get('/api/v1/doors/:id', DoorService.findById);
app.post('/api/v1/doors', DoorService.add);
app.put('/api/v1/doors/:id', DoorService.update3); // added {upsert:true} option
app.delete('/api/v1/doors/:id', DoorService.delete);
app.get('/api/v1/doors/project/:pid', DoorService.findAllForProject);
}
</pre>
<p>The updated implementation is captured in
<a href="https://github.com/jeremytammik/firerating/releases/tag/0.0.14">fireratingdb 0.0.14</a>
– implemented DoorService class to replace individual separate module.exports functions.</p>
<p>To wrap it up, I also added a version number to the various 'hello' messages and published
<a href="https://github.com/jeremytammik/firerating/releases/tag/0.0.15">fireratingdb 0.0.15</a>.</p>
<p>As always, I redeployed the web server to
<a href="https://www.heroku.com">heroku</a> for global access, where it continues running at
<a href="http://fireratingdb.herokuapp.com">fireratingdb.herokuapp.com</a>.</p>
<p>Thanks again to Philippe for his helpful and illuminating suggestions!</p>