Ravn::Tactical::ServiceV1::

Missions module

:nocov:

Public Class Methods

included( app )

Add mission-related routes to the given app.

   # File lib/ravn/tactical/service_v1/missions.rb
23 def self::included( app )
24     super
25 
26     self.log.info "Mounting mission endpoints."
27     self.mount( app )
28 end
mount( app )

Mount mission endpoints in the given app.

   # File lib/ravn/tactical/service_v1/missions.rb
32 def self::mount( app )
33     app.hash_branch( :v1, 'missions' ) do |r|
34 
35         # GET  /api/v1/missions
36         # POST /api/v1/missions
37         r.is do
38             r.get( &self.method(:get_missions) )
39             r.post( &self.method(:create_mission) )
40         end
41 
42         # GET /api/v1/missions/current
43         r.on( 'current' ) { self.get_current_mission }
44 
45         # POST   /api/v1/missions/«UUID»/make_current
46         # POST   /api/v1/missions/«UUID»/archive
47         # GET    /api/v1/missions/«UUID»/archive
48         # POST   /api/v1/missions/«UUID»/purge
49         # GET    /api/v1/missions/«UUID»
50         # PUT    /api/v1/missions/«UUID»
51         # POST   /api/v1/missions/«UUID»
52         # DELETE /api/v1/missions/«UUID»
53         r.on( /(#{UUID_PATTERN})/ ) do |uuid|
54             self.log.debug "Looking for endpoint for mission %p" % [ uuid ]
55 
56             r.post( 'make_current' ) { self.make_current_mission(uuid) }
57             r.post( 'purge' ) { self.purge_mission(uuid) }
58 
59             r.on( 'archive' ) do
60                 r.get { self.download_archive(uuid) }
61                 r.post { self.archive_mission(uuid) }
62             end
63 
64             r.get { self.get_mission(uuid) }
65             r.put { self.replace_mission(uuid) }
66             r.post { self.update_mission(uuid) }
67             r.delete { self.delete_mission(uuid) }
68         end
69 
70     end
71 end

Public Instance Methods

archive_mission( uuid )

Archive the mission with the given uuid (which must be the current mission).

    # File lib/ravn/tactical/service_v1/missions.rb
211 def archive_mission( uuid )
212     self.log.info "User requested mission %s be archived." % [ uuid ]
213     mission = Ravn::Tactical::Mission.fetch( uuid ) or return nil
214 
215     unless mission.current?
216         response.status = :conflict
217         return { status: 'Only the current mission may be archived.' }
218     end
219 
220     Ravn::Tactical::Kit.archive_mission( mission )
221 
222     response.status = :accepted
223     return { status: 'Mission is being archived.' }
224 end
create_mission()

Create a new mission.

    # File lib/ravn/tactical/service_v1/missions.rb
 82 def create_mission
 83     self.log.info "Creating mission from request: %p." % [ request.body.string ]
 84 
 85     params = typecast_params.convert!( symbolize: true ) do |tp|
 86         tp.uuid( 'clone' )
 87     end
 88 
 89     if (uuid = params[:clone])
 90         original = Ravn::Tactical::Mission.fetch( uuid ) or return nil
 91         mission = original.clone.write
 92     else
 93         mission = Ravn::Tactical::Mission.create_with_serial_name.write
 94     end
 95 
 96     response.status = 201
 97     response['Location'] = url( mission )
 98 
 99     return present( mission )
100 end
delete_mission( uuid )

Delete any mission config file for the given uuid.

    # File lib/ravn/tactical/service_v1/missions.rb
188 def delete_mission( uuid )
189     config_path = Ravn::BDE.mission_directory + Ravn::Tactical::Mission.filename_for( uuid )
190 
191     config_path.unlink if config_path.exist?
192 
193     return { status: "Mission was deleted." }
194 end
download_archive( uuid )

Stream a tarball for a mission archive at uuid.

    # File lib/ravn/tactical/service_v1/missions.rb
228 def download_archive( uuid )
229     archive_path = Ravn::Tactical::Mission.archive_directory
230     mission      = Ravn::Tactical::Mission.fetch( uuid ) or return nil
231 
232     unless mission.archive_directory_path.exist?
233         response.status = :bad_request
234         return
235     end
236 
237     response.status = :ok
238     response[ 'Content-Type' ] = 'application/octet-stream'
239     response[ 'Content-Disposition' ] = "attachment; filename=\"%s-%s.tar\"" % [
240         mission.name, Time.now.to_i
241     ]
242 
243     files = mission.device_archive_files
244     self.log.info "Streaming tar archive with %d file/s" % [ files.size ]
245 
246     return stream do |raw_out|
247         out = Archive::Tar::Minitar::Output.new( raw_out )
248         Dir.chdir( archive_path ) do
249             files.each do |file|
250                 file = file.relative_path_from( archive_path )
251                 self.log.debug "  adding %s" % [ file ]
252                 Archive::Tar::Minitar.pack_file( file.to_s, out ) do |action, fn, stats|
253                     self.log.debug "%s: %s %p" % [ action, fn, stats ]
254                 end
255             end
256         end
257     end
258 end
get_current_mission()

Fetch the current mission.

    # File lib/ravn/tactical/service_v1/missions.rb
114 def get_current_mission
115     self.log.info "Fetching the current mission"
116 
117     mission = Ravn::Tactical::Mission.current
118 
119     if mission
120         return present( mission )
121     else
122         response.status = :no_content
123         return ''
124     end
125 end
get_mission( uuid )

Fetch a single mission by ID.

    # File lib/ravn/tactical/service_v1/missions.rb
104 def get_mission( uuid )
105     self.log.info "Fetching mission %s" % [ uuid ]
106 
107     mission = Ravn::Tactical::Mission.fetch( uuid ) or return nil
108 
109     return present( mission )
110 end
get_missions()

Fetch a collection of the current mission IDs that are on the compute pack.

   # File lib/ravn/tactical/service_v1/missions.rb
75 def get_missions
76     missions = Ravn::Tactical::Mission.all
77     present_collection( missions )
78 end
make_current_mission( uuid )

Set the mission with the given uuid as the current mission, archiving any prior mission.

    # File lib/ravn/tactical/service_v1/missions.rb
199 def make_current_mission( uuid )
200     self.log.info "User requested mission %s be made the current one." % [ uuid ]
201     new_mission = Ravn::Tactical::Mission.fetch( uuid ) or return nil
202 
203     Ravn::Tactical::Kit.set_mission( new_mission )
204 
205     response.status = :accepted
206     return { status: 'Mission change accepted.', checksum: new_mission.checksum }
207 end
purge_mission( uuid )

Remove the mission with the given uuid from disk.

    # File lib/ravn/tactical/service_v1/missions.rb
262 def purge_mission( uuid )
263     self.log.info "User requested mission %s be purged." % [ uuid ]
264     mission = Ravn::Tactical::Mission.fetch( uuid ) or return nil
265 
266     Ravn::Tactical::Kit.purge_mission( mission )
267 
268     response.status = :accepted
269     return { status: 'Mission has been purged.' }
270 end
replace_mission( uuid )

Replace the data from the mission with the request’s parameters.

    # File lib/ravn/tactical/service_v1/missions.rb
129 def replace_mission( uuid )
130     self.log.info "Replacing mission %s" % [ uuid ]
131 
132     params = valid_mission_parameters()
133     params = Ravn::DataUtilities.symbolify_keys( params )
134     self.log.debug "Params are: %p" % [ params ]
135 
136     params[:id] = uuid
137     name = params.delete( :name ) or raise "No name in valid params?"
138 
139     mission = Ravn::Tactical::Mission.create( name, **params )
140     return bad_request( request, "Invalid mission config." ) unless mission.valid?
141 
142     replace = mission.filepath.exist?
143     mission.write( replace )
144 
145     Ravn::Tactical::Kit.set_mission( mission ) if mission.current?
146 
147     return present( mission )
148 end
replace_nodes( uuid )

Replace the callsigns map for the mission with one made from the nodes from the Array of device IDs in the body of the request.

    # File lib/ravn/tactical/service_v1/missions.rb
153 def replace_nodes( uuid )
154     mission = Ravn::Tactical::Mission.fetch( uuid ) or return nil
155     request_data = Yajl::Parser.parse( request.body )
156 
157     raise ArgumentError, "malformed PUT body: expected an Array of device IDs" unless
158         request_data.is_a?( Array )
159 
160     new_device_ids = request_data.select {|id| id.match?(/\A\h{4}\z/) }
161     nodes = Ravn::Tactical::Node.where( device_id: new_device_ids ).all
162     mission.nodes = nodes
163     mission.write
164 
165     return ''
166 end
update_mission( uuid )

Update an existing mission with the request’s parameters.

    # File lib/ravn/tactical/service_v1/missions.rb
170 def update_mission( uuid )
171     self.log.info "Updating mission %s" % [ uuid ]
172 
173     params = valid_mission_parameters()
174 
175     mission = Ravn::Tactical::Mission.fetch( uuid ) or return nil
176     mission.update( **params )
177     return bad_request( "Invalid mission config." ) unless mission.valid?
178 
179     mission.write( true )
180 
181     Ravn::Tactical::Kit.set_mission( mission ) if mission.current?
182 
183     return present( mission )
184 end

Protected Instance Methods

valid_mission_parameters()

Validate the current request body against parameters for Mission objects. See the the typecast_params plugin for details.

Refs: - roda.jeremyevans.net/rdoc/classes/Roda/RodaPlugins/TypecastParams.html

    # File lib/ravn/tactical/service_v1/missions.rb
282 def valid_mission_parameters
283     return typecast_params.convert!( symbolize: true ) do |tp|
284         tp.pos_int( 'format_version', Ravn::Tactical::Mission::DEFAULT_FORMAT_VERSION )
285 
286         tp.nonempty_str!( 'name' )
287 
288         # :TODO: Nested untainting
289         tp.Hash( 'bolts', {} )
290         tp.Hash( 'callsigns', {} )
291         tp.array( :Hash, 'pois', [] )
292         tp.Hash( 'segments', {} )
293     end
294 end