update versions under scenario
[releng.git] / utils / test / testapi / opnfv_testapi / resources / scenario_handlers.py
1 import functools
2
3 from opnfv_testapi.common import message
4 from opnfv_testapi.common import raises
5 from opnfv_testapi.resources import handlers
6 import opnfv_testapi.resources.scenario_models as models
7 from opnfv_testapi.tornado_swagger import swagger
8
9
10 class GenericScenarioHandler(handlers.GenericApiHandler):
11     def __init__(self, application, request, **kwargs):
12         super(GenericScenarioHandler, self).__init__(application,
13                                                      request,
14                                                      **kwargs)
15         self.table = self.db_scenarios
16         self.table_cls = models.Scenario
17
18     def set_query(self, locators):
19         query = dict()
20         elem_query = dict()
21         for k, v in locators.iteritems():
22             if k == 'scenario':
23                 query['name'] = v
24             elif k == 'installer':
25                 elem_query["installer"] = v
26             elif k == 'version':
27                 elem_query["versions.version"] = v
28             elif k == 'project':
29                 elem_query["versions.projects.project"] = v
30             else:
31                 query[k] = v
32         if elem_query:
33             query['installers'] = {'$elemMatch': elem_query}
34         return query
35
36
37 class ScenariosCLHandler(GenericScenarioHandler):
38     @swagger.operation(nickname="queryScenarios")
39     def get(self):
40         """
41             @description: Retrieve scenario(s).
42             @notes: Retrieve scenario(s)
43                 Available filters for this request are :
44                  - name : scenario name
45
46                 GET /scenarios?name=scenario_1
47             @param name: scenario name
48             @type name: L{string}
49             @in name: query
50             @required name: False
51             @param installer: installer type
52             @type installer: L{string}
53             @in installer: query
54             @required installer: False
55             @param version: version
56             @type version: L{string}
57             @in version: query
58             @required version: False
59             @param project: project name
60             @type project: L{string}
61             @in project: query
62             @required project: False
63             @return 200: all scenarios satisfy queries,
64                          empty list if no scenario is found
65             @rtype: L{Scenarios}
66         """
67
68         def _set_query():
69             query = dict()
70             elem_query = dict()
71             for k in self.request.query_arguments.keys():
72                 v = self.get_query_argument(k)
73                 if k == 'installer':
74                     elem_query["installer"] = v
75                 elif k == 'version':
76                     elem_query["versions.version"] = v
77                 elif k == 'project':
78                     elem_query["versions.projects.project"] = v
79                 else:
80                     query[k] = v
81             if elem_query:
82                 query['installers'] = {'$elemMatch': elem_query}
83             return query
84
85         self._list(query=_set_query())
86
87     @swagger.operation(nickname="createScenario")
88     def post(self):
89         """
90             @description: create a new scenario by name
91             @param body: scenario to be created
92             @type body: L{ScenarioCreateRequest}
93             @in body: body
94             @rtype: L{CreateResponse}
95             @return 200: scenario is created.
96             @raise 403: scenario already exists
97             @raise 400:  body or name not provided
98         """
99         def query():
100             return {'name': self.json_args.get('name')}
101         miss_fields = ['name']
102         self._create(miss_fields=miss_fields, query=query)
103
104
105 class ScenarioGURHandler(GenericScenarioHandler):
106     @swagger.operation(nickname='getScenarioByName')
107     def get(self, name):
108         """
109             @description: get a single scenario by name
110             @rtype: L{Scenario}
111             @return 200: scenario exist
112             @raise 404: scenario not exist
113         """
114         self._get_one(query={'name': name})
115         pass
116
117     def put(self, name):
118         pass
119
120     @swagger.operation(nickname="deleteScenarioByName")
121     def delete(self, name):
122         """
123         @description: delete a scenario by name
124         @return 200: delete success
125         @raise 404: scenario not exist:
126         """
127         self._delete(query={'name': name})
128
129
130 class ScenarioUpdater(object):
131     def __init__(self, data, body=None,
132                  installer=None, version=None, project=None):
133         self.data = data
134         self.body = body
135         self.installer = installer
136         self.version = version
137         self.project = project
138
139     def update(self, item, action):
140         updates = {
141             ('scores', 'post'): self._update_requests_add_score,
142             ('trust_indicators', 'post'): self._update_requests_add_ti,
143             ('customs', 'post'): self._update_requests_add_customs,
144             ('customs', 'put'): self._update_requests_update_customs,
145             ('customs', 'delete'): self._update_requests_delete_customs,
146             ('projects', 'post'): self._update_requests_add_projects,
147             ('projects', 'put'): self._update_requests_update_projects,
148             ('projects', 'delete'): self._update_requests_delete_projects,
149             ('owner', 'put'): self._update_requests_change_owner,
150             ('versions', 'post'): self._update_requests_add_versions,
151             ('versions', 'put'): self._update_requests_update_versions,
152             ('versions', 'delete'): self._update_requests_delete_versions,
153         }
154         updates[(item, action)](self.data)
155
156         return self.data.format()
157
158     def iter_installers(xstep):
159         @functools.wraps(xstep)
160         def magic(self, data):
161             [xstep(self, installer)
162              for installer in self._filter_installers(data.installers)]
163         return magic
164
165     def iter_versions(xstep):
166         @functools.wraps(xstep)
167         def magic(self, installer):
168             [xstep(self, version)
169              for version in (self._filter_versions(installer.versions))]
170         return magic
171
172     def iter_projects(xstep):
173         @functools.wraps(xstep)
174         def magic(self, version):
175             [xstep(self, project)
176              for project in (self._filter_projects(version.projects))]
177         return magic
178
179     @iter_installers
180     @iter_versions
181     @iter_projects
182     def _update_requests_add_score(self, project):
183         project.scores.append(
184             models.ScenarioScore.from_dict(self.body))
185
186     @iter_installers
187     @iter_versions
188     @iter_projects
189     def _update_requests_add_ti(self, project):
190         project.trust_indicators.append(
191             models.ScenarioTI.from_dict(self.body))
192
193     @iter_installers
194     @iter_versions
195     @iter_projects
196     def _update_requests_add_customs(self, project):
197         project.customs = list(set(project.customs + self.body))
198
199     @iter_installers
200     @iter_versions
201     @iter_projects
202     def _update_requests_update_customs(self, project):
203         project.customs = list(set(self.body))
204
205     @iter_installers
206     @iter_versions
207     @iter_projects
208     def _update_requests_delete_customs(self, project):
209         project.customs = filter(
210             lambda f: f not in self.body,
211             project.customs)
212
213     @iter_installers
214     @iter_versions
215     def _update_requests_add_projects(self, version):
216         version.projects = self._update_with_body(models.ScenarioProject,
217                                                   'project',
218                                                   version.projects)
219
220     @iter_installers
221     @iter_versions
222     def _update_requests_update_projects(self, version):
223         version.projects = self._update_with_body(models.ScenarioProject,
224                                                   'project',
225                                                   list())
226
227     @iter_installers
228     @iter_versions
229     def _update_requests_delete_projects(self, version):
230         version.projects = self._remove_projects(version.projects)
231
232     @iter_installers
233     @iter_versions
234     def _update_requests_change_owner(self, version):
235         version.owner = self.body.get('owner')
236
237     @iter_installers
238     def _update_requests_add_versions(self, installer):
239         installer.versions = self._update_with_body(models.ScenarioVersion,
240                                                     'version',
241                                                     installer.versions)
242
243     @iter_installers
244     def _update_requests_update_versions(self, installer):
245         installer.versions = self._update_with_body(models.ScenarioVersion,
246                                                     'version',
247                                                     list())
248
249     @iter_installers
250     def _update_requests_delete_versions(self, installer):
251         installer.versions = self._remove_versions(installer.versions)
252
253     def _update_with_body(self, clazz, field, withs):
254         exists = list()
255         malformat = list()
256         for new in self.body:
257             try:
258                 format_new = clazz.from_dict_with_raise(new)
259                 new_name = getattr(format_new, field)
260                 if not any(getattr(o, field) == new_name for o in withs):
261                     withs.append(format_new)
262                 else:
263                     exists.append(new_name)
264             except Exception as error:
265                 malformat.append(error.message)
266         if malformat:
267             raises.BadRequest(message.bad_format(malformat))
268         elif exists:
269             raises.Conflict(message.exist('{}s'.format(field), exists))
270         return withs
271
272     def _filter_installers(self, installers):
273         return self._filter('installer', installers)
274
275     def _filter_versions(self, versions):
276         return self._filter('version', versions)
277
278     def _remove_versions(self, versions):
279         return self._remove('version', versions)
280
281     def _filter_projects(self, projects):
282         return self._filter('project', projects)
283
284     def _remove_projects(self, projects):
285         return self._remove('project', projects)
286
287     def _filter(self, item, items):
288         return filter(
289             lambda f: getattr(f, item) == getattr(self, item),
290             items)
291
292     def _remove(self, field, fields):
293         return filter(
294             lambda f: getattr(f, field) not in self.body,
295             fields)
296
297
298 class GenericScenarioUpdateHandler(GenericScenarioHandler):
299     def __init__(self, application, request, **kwargs):
300         super(GenericScenarioUpdateHandler, self).__init__(application,
301                                                            request,
302                                                            **kwargs)
303         self.installer = None
304         self.version = None
305         self.project = None
306         self.item = None
307         self.action = None
308
309     def do_update(self, item, action, locators):
310         self.item = item
311         self.action = action
312         for k, v in locators.iteritems():
313             if not v:
314                 v = self.get_query_argument(k)
315                 setattr(self, k, v)
316                 locators[k] = v
317         self.pure_update(query=self.set_query(locators=locators))
318
319     def _update_requests(self, data):
320         return ScenarioUpdater(data,
321                                self.json_args,
322                                self.installer,
323                                self.version,
324                                self.project).update(self.item, self.action)
325
326
327 class ScenarioScoresHandler(GenericScenarioUpdateHandler):
328     @swagger.operation(nickname="addScoreRecord")
329     def post(self, scenario):
330         """
331         @description: add a new score record
332         @notes: add a new score record to a project
333             POST /api/v1/scenarios/<scenario_name>/scores? \
334                 installer=<installer_name>& \
335                 version=<version_name>& \
336                 project=<project_name>
337         @param body: score to be added
338         @type body: L{ScenarioScore}
339         @in body: body
340         @param installer: installer type
341         @type installer: L{string}
342         @in installer: query
343         @required installer: True
344         @param version: version
345         @type version: L{string}
346         @in version: query
347         @required version: True
348         @param project: project name
349         @type project: L{string}
350         @in project: query
351         @required project: True
352         @return 200: score is created.
353         @raise 404:  scenario/installer/version/project not existed
354         """
355         self.do_update('scores',
356                        'post',
357                        locators={'scenario': scenario,
358                                  'installer': None,
359                                  'version': None,
360                                  'project': None})
361
362
363 class ScenarioTIsHandler(GenericScenarioUpdateHandler):
364     @swagger.operation(nickname="addTrustIndicatorRecord")
365     def post(self, scenario):
366         """
367         @description: add a new trust indicator record
368         @notes: add a new trust indicator record to a project
369             POST /api/v1/scenarios/<scenario_name>/trust_indicators? \
370                 installer=<installer_name>& \
371                 version=<version_name>& \
372                 project=<project_name>
373         @param body: trust indicator to be added
374         @type body: L{ScenarioTI}
375         @in body: body
376         @param installer: installer type
377         @type installer: L{string}
378         @in installer: query
379         @required installer: True
380         @param version: version
381         @type version: L{string}
382         @in version: query
383         @required version: True
384         @param project: project name
385         @type project: L{string}
386         @in project: query
387         @required project: True
388         @return 200: trust indicator is added.
389         @raise 404:  scenario/installer/version/project not existed
390         """
391         self.do_update('trust_indicators',
392                        'post',
393                        locators={'scenario': scenario,
394                                  'installer': None,
395                                  'version': None,
396                                  'project': None})
397
398
399 class ScenarioCustomsHandler(GenericScenarioUpdateHandler):
400     @swagger.operation(nickname="addCustomizedTestCases")
401     def post(self, scenario):
402         """
403         @description: add customized test cases
404         @notes: add several test cases to a project
405             POST /api/v1/scenarios/<scenario_name>/customs? \
406                 installer=<installer_name>& \
407                 version=<version_name>& \
408                 project=<project_name>
409         @param body: test cases to be added
410         @type body: C{list} of L{string}
411         @in body: body
412         @param installer: installer type
413         @type installer: L{string}
414         @in installer: query
415         @required installer: True
416         @param version: version
417         @type version: L{string}
418         @in version: query
419         @required version: True
420         @param project: project name
421         @type project: L{string}
422         @in project: query
423         @required project: True
424         @return 200: test cases are added.
425         @raise 404:  scenario/installer/version/project not existed
426         """
427         self.do_update('customs',
428                        'post',
429                        locators={'scenario': scenario,
430                                  'installer': None,
431                                  'version': None,
432                                  'project': None})
433
434     @swagger.operation(nickname="updateCustomizedTestCases")
435     def put(self, scenario):
436         """
437         @description: update customized test cases
438         @notes: substitute all the customized test cases
439             PUT /api/v1/scenarios/<scenario_name>/customs? \
440                 installer=<installer_name>& \
441                 version=<version_name>& \
442                 project=<project_name>
443         @param body: new supported test cases
444         @type body: C{list} of L{string}
445         @in body: body
446         @param installer: installer type
447         @type installer: L{string}
448         @in installer: query
449         @required installer: True
450         @param version: version
451         @type version: L{string}
452         @in version: query
453         @required version: True
454         @param project: project name
455         @type project: L{string}
456         @in project: query
457         @required project: True
458         @return 200: substitute test cases success.
459         @raise 404:  scenario/installer/version/project not existed
460         """
461         self.do_update('customs',
462                        'put',
463                        locators={'scenario': scenario,
464                                  'installer': None,
465                                  'version': None,
466                                  'project': None})
467
468     @swagger.operation(nickname="deleteCustomizedTestCases")
469     def delete(self, scenario):
470         """
471         @description: delete one or several customized test cases
472         @notes: delete one or some customized test cases
473             DELETE /api/v1/scenarios/<scenario_name>/customs? \
474                 installer=<installer_name>& \
475                 version=<version_name>& \
476                 project=<project_name>
477         @param body: test case(s) to be deleted
478         @type body: C{list} of L{string}
479         @in body: body
480         @param installer: installer type
481         @type installer: L{string}
482         @in installer: query
483         @required installer: True
484         @param version: version
485         @type version: L{string}
486         @in version: query
487         @required version: True
488         @param project: project name
489         @type project: L{string}
490         @in project: query
491         @required project: True
492         @return 200: delete test case(s) success.
493         @raise 404:  scenario/installer/version/project not existed
494         """
495         self.do_update('customs',
496                        'delete',
497                        locators={'scenario': scenario,
498                                  'installer': None,
499                                  'version': None,
500                                  'project': None})
501
502
503 class ScenarioProjectsHandler(GenericScenarioUpdateHandler):
504     @swagger.operation(nickname="addProjectsUnderScenario")
505     def post(self, scenario):
506         """
507         @description: add projects to scenario
508         @notes: add one or multiple projects
509             POST /api/v1/scenarios/<scenario_name>/projects? \
510                 installer=<installer_name>& \
511                 version=<version_name>
512         @param body: projects to be added
513         @type body: C{list} of L{ScenarioProject}
514         @in body: body
515         @param installer: installer type
516         @type installer: L{string}
517         @in installer: query
518         @required installer: True
519         @param version: version
520         @type version: L{string}
521         @in version: query
522         @required version: True
523         @return 200: projects are added.
524         @raise 400: bad schema
525         @raise 409: conflict, project already exists
526         @raise 404:  scenario/installer/version not existed
527         """
528         self.do_update('projects',
529                        'post',
530                        locators={'scenario': scenario,
531                                  'installer': None,
532                                  'version': None})
533
534     @swagger.operation(nickname="updateScenarioProjects")
535     def put(self, scenario):
536         """
537         @description: replace all projects
538         @notes: substitute all projects, delete existed ones with new provides
539             PUT /api/v1/scenarios/<scenario_name>/projects? \
540                 installer=<installer_name>& \
541                 version=<version_name>
542         @param body: new projects
543         @type body: C{list} of L{ScenarioProject}
544         @in body: body
545         @param installer: installer type
546         @type installer: L{string}
547         @in installer: query
548         @required installer: True
549         @param version: version
550         @type version: L{string}
551         @in version: query
552         @required version: True
553         @return 200: replace projects success.
554         @raise 400: bad schema
555         @raise 404:  scenario/installer/version not existed
556         """
557         self.do_update('projects',
558                        'put',
559                        locators={'scenario': scenario,
560                                  'installer': None,
561                                  'version': None})
562
563     @swagger.operation(nickname="deleteProjectsUnderScenario")
564     def delete(self, scenario):
565         """
566         @description: delete one or multiple projects
567         @notes: delete one or multiple projects
568             DELETE /api/v1/scenarios/<scenario_name>/projects? \
569                 installer=<installer_name>& \
570                 version=<version_name>
571         @param body: projects(names) to be deleted
572         @type body: C{list} of L{string}
573         @in body: body
574         @param installer: installer type
575         @type installer: L{string}
576         @in installer: query
577         @required installer: True
578         @param version: version
579         @type version: L{string}
580         @in version: query
581         @required version: True
582         @return 200: delete project(s) success.
583         @raise 404:  scenario/installer/version not existed
584         """
585         self.do_update('projects',
586                        'delete',
587                        locators={'scenario': scenario,
588                                  'installer': None,
589                                  'version': None})
590
591
592 class ScenarioOwnerHandler(GenericScenarioUpdateHandler):
593     @swagger.operation(nickname="changeScenarioOwner")
594     def put(self, scenario):
595         """
596         @description: change scenario owner
597         @notes: substitute all projects, delete existed ones with new provides
598             PUT /api/v1/scenarios/<scenario_name>/owner? \
599                 installer=<installer_name>& \
600                 version=<version_name>
601         @param body: new owner
602         @type body: L{ScenarioChangeOwnerRequest}
603         @in body: body
604         @param installer: installer type
605         @type installer: L{string}
606         @in installer: query
607         @required installer: True
608         @param version: version
609         @type version: L{string}
610         @in version: query
611         @required version: True
612         @return 200: change owner success.
613         @raise 404:  scenario/installer/version not existed
614         """
615         self.do_update('owner',
616                        'put',
617                        locators={'scenario': scenario,
618                                  'installer': None,
619                                  'version': None})
620
621
622 class ScenarioVersionsHandler(GenericScenarioUpdateHandler):
623     @swagger.operation(nickname="addVersionsUnderScenario")
624     def post(self, scenario):
625         """
626         @description: add versions to scenario
627         @notes: add one or multiple versions
628             POST /api/v1/scenarios/<scenario_name>/versions? \
629                 installer=<installer_name>
630         @param body: versions to be added
631         @type body: C{list} of L{ScenarioVersion}
632         @in body: body
633         @param installer: installer type
634         @type installer: L{string}
635         @in installer: query
636         @required installer: True
637         @return 200: versions are added.
638         @raise 400: bad schema
639         @raise 409: conflict, version already exists
640         @raise 404:  scenario/installer not exist
641         """
642         self.do_update('versions',
643                        'post',
644                        locators={'scenario': scenario,
645                                  'installer': None})
646
647     @swagger.operation(nickname="updateVersionsUnderScenario")
648     def put(self, scenario):
649         """
650         @description: replace all versions
651         @notes: substitute all versions as a totality
652             PUT /api/v1/scenarios/<scenario_name>/versions? \
653                 installer=<installer_name>
654         @param body: new versions
655         @type body: C{list} of L{ScenarioVersion}
656         @in body: body
657         @param installer: installer type
658         @type installer: L{string}
659         @in installer: query
660         @required installer: True
661         @return 200: replace versions success.
662         @raise 400: bad schema
663         @raise 404:  scenario/installer not exist
664         """
665         self.do_update('versions',
666                        'put',
667                        locators={'scenario': scenario,
668                                  'installer': None})
669
670     @swagger.operation(nickname="deleteVersionsUnderScenario")
671     def delete(self, scenario):
672         """
673         @description: delete one or multiple versions
674         @notes: delete one or multiple versions
675             DELETE /api/v1/scenarios/<scenario_name>/versions? \
676                 installer=<installer_name>
677         @param body: versions(names) to be deleted
678         @type body: C{list} of L{string}
679         @in body: body
680         @param installer: installer type
681         @type installer: L{string}
682         @in installer: query
683         @required installer: True
684         @return 200: delete versions success.
685         @raise 404:  scenario/installer not exist
686         """
687         self.do_update('versions',
688                        'delete',
689                        locators={'scenario': scenario,
690                                  'installer': None})