Merge "update projects in 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         }
150         updates[(item, action)](self.data)
151
152         return self.data.format()
153
154     def iter_installers(xstep):
155         @functools.wraps(xstep)
156         def magic(self, data):
157             [xstep(self, installer)
158              for installer in self._filter_installers(data.installers)]
159         return magic
160
161     def iter_versions(xstep):
162         @functools.wraps(xstep)
163         def magic(self, installer):
164             [xstep(self, version)
165              for version in (self._filter_versions(installer.versions))]
166         return magic
167
168     def iter_projects(xstep):
169         @functools.wraps(xstep)
170         def magic(self, version):
171             [xstep(self, project)
172              for project in (self._filter_projects(version.projects))]
173         return magic
174
175     @iter_installers
176     @iter_versions
177     @iter_projects
178     def _update_requests_add_score(self, project):
179         project.scores.append(
180             models.ScenarioScore.from_dict(self.body))
181
182     @iter_installers
183     @iter_versions
184     @iter_projects
185     def _update_requests_add_ti(self, project):
186         project.trust_indicators.append(
187             models.ScenarioTI.from_dict(self.body))
188
189     @iter_installers
190     @iter_versions
191     @iter_projects
192     def _update_requests_add_customs(self, project):
193         project.customs = list(set(project.customs + self.body))
194
195     @iter_installers
196     @iter_versions
197     @iter_projects
198     def _update_requests_update_customs(self, project):
199         project.customs = list(set(self.body))
200
201     @iter_installers
202     @iter_versions
203     @iter_projects
204     def _update_requests_delete_customs(self, project):
205         project.customs = filter(
206             lambda f: f not in self.body,
207             project.customs)
208
209     @iter_installers
210     @iter_versions
211     def _update_requests_add_projects(self, version):
212         exists = list()
213         malformat = list()
214         for n in self.body:
215             try:
216                 f_n = models.ScenarioProject.from_dict_with_raise(n)
217                 if not any(o.project == f_n.project for o in version.projects):
218                     version.projects.append(f_n)
219                 else:
220                     exists.append(n['project'])
221             except Exception as e:
222                 malformat.append(e.message)
223         if malformat:
224             raises.BadRequest(message.bad_format(malformat))
225         elif exists:
226             raises.Conflict(message.exist('projects', exists))
227
228     @iter_installers
229     @iter_versions
230     def _update_requests_update_projects(self, version):
231         exists = list()
232         malformat = list()
233         projects = list()
234         for n in self.body:
235             try:
236                 f_n = models.ScenarioProject.from_dict_with_raise(n)
237                 if not any(o.project == f_n.project for o in projects):
238                     projects.append(models.ScenarioProject.from_dict(n))
239                 else:
240                     exists.append(n['project'])
241             except:
242                 malformat.append(n)
243         if malformat:
244             raises.BadRequest(message.bad_format(malformat))
245         elif exists:
246             raises.Forbidden(message.exist('projects', exists))
247         version.projects = projects
248
249     @iter_installers
250     @iter_versions
251     def _update_requests_delete_projects(self, version):
252         version.projects = self._remove_projects(version.projects)
253
254     def _filter_installers(self, installers):
255         return self._filter('installer', installers)
256
257     def _filter_versions(self, versions):
258         return self._filter('version', versions)
259
260     def _filter_projects(self, projects):
261         return self._filter('project', projects)
262
263     def _remove_projects(self, projects):
264         return self._remove('project', projects)
265
266     def _filter(self, item, items):
267         return filter(
268             lambda f: getattr(f, item) == getattr(self, item),
269             items)
270
271     def _remove(self, field, fields):
272         return filter(
273             lambda f: getattr(f, field) not in self.body,
274             fields)
275
276
277 class GenericScenarioUpdateHandler(GenericScenarioHandler):
278     def __init__(self, application, request, **kwargs):
279         super(GenericScenarioUpdateHandler, self).__init__(application,
280                                                            request,
281                                                            **kwargs)
282         self.installer = None
283         self.version = None
284         self.project = None
285         self.item = None
286         self.action = None
287
288     def do_update(self, item, action, locators):
289         self.item = item
290         self.action = action
291         for k, v in locators.iteritems():
292             if not v:
293                 v = self.get_query_argument(k)
294                 setattr(self, k, v)
295                 locators[k] = v
296         self.pure_update(query=self.set_query(locators=locators))
297
298     def _update_requests(self, data):
299         return ScenarioUpdater(data,
300                                self.json_args,
301                                self.installer,
302                                self.version,
303                                self.project).update(self.item, self.action)
304
305
306 class ScenarioScoresHandler(GenericScenarioUpdateHandler):
307     @swagger.operation(nickname="addScoreRecord")
308     def post(self, scenario):
309         """
310         @description: add a new score record
311         @notes: add a new score record to a project
312             POST /api/v1/scenarios/<scenario_name>/scores? \
313                 installer=<installer_name>& \
314                 version=<version_name>& \
315                 project=<project_name>
316         @param body: score to be added
317         @type body: L{ScenarioScore}
318         @in body: body
319         @param installer: installer type
320         @type installer: L{string}
321         @in installer: query
322         @required installer: True
323         @param version: version
324         @type version: L{string}
325         @in version: query
326         @required version: True
327         @param project: project name
328         @type project: L{string}
329         @in project: query
330         @required project: True
331         @return 200: score is created.
332         @raise 404:  scenario/installer/version/project not existed
333         """
334         self.do_update('scores',
335                        'post',
336                        locators={'scenario': scenario,
337                                  'installer': None,
338                                  'version': None,
339                                  'project': None})
340
341
342 class ScenarioTIsHandler(GenericScenarioUpdateHandler):
343     @swagger.operation(nickname="addTrustIndicatorRecord")
344     def post(self, scenario):
345         """
346         @description: add a new trust indicator record
347         @notes: add a new trust indicator record to a project
348             POST /api/v1/scenarios/<scenario_name>/trust_indicators? \
349                 installer=<installer_name>& \
350                 version=<version_name>& \
351                 project=<project_name>
352         @param body: trust indicator to be added
353         @type body: L{ScenarioTI}
354         @in body: body
355         @param installer: installer type
356         @type installer: L{string}
357         @in installer: query
358         @required installer: True
359         @param version: version
360         @type version: L{string}
361         @in version: query
362         @required version: True
363         @param project: project name
364         @type project: L{string}
365         @in project: query
366         @required project: True
367         @return 200: trust indicator is added.
368         @raise 404:  scenario/installer/version/project not existed
369         """
370         self.do_update('trust_indicators',
371                        'post',
372                        locators={'scenario': scenario,
373                                  'installer': None,
374                                  'version': None,
375                                  'project': None})
376
377
378 class ScenarioCustomsHandler(GenericScenarioUpdateHandler):
379     @swagger.operation(nickname="addCustomizedTestCases")
380     def post(self, scenario):
381         """
382         @description: add customized test cases
383         @notes: add several test cases to a project
384             POST /api/v1/scenarios/<scenario_name>/customs? \
385                 installer=<installer_name>& \
386                 version=<version_name>& \
387                 project=<project_name>
388         @param body: test cases to be added
389         @type body: C{list} of L{string}
390         @in body: body
391         @param installer: installer type
392         @type installer: L{string}
393         @in installer: query
394         @required installer: True
395         @param version: version
396         @type version: L{string}
397         @in version: query
398         @required version: True
399         @param project: project name
400         @type project: L{string}
401         @in project: query
402         @required project: True
403         @return 200: test cases are added.
404         @raise 404:  scenario/installer/version/project not existed
405         """
406         self.do_update('customs',
407                        'post',
408                        locators={'scenario': scenario,
409                                  'installer': None,
410                                  'version': None,
411                                  'project': None})
412
413     @swagger.operation(nickname="updateCustomizedTestCases")
414     def put(self, scenario):
415         """
416         @description: update customized test cases
417         @notes: substitute all the customized test cases
418             PUT /api/v1/scenarios/<scenario_name>/customs? \
419                 installer=<installer_name>& \
420                 version=<version_name>& \
421                 project=<project_name>
422         @param body: new supported test cases
423         @type body: C{list} of L{string}
424         @in body: body
425         @param installer: installer type
426         @type installer: L{string}
427         @in installer: query
428         @required installer: True
429         @param version: version
430         @type version: L{string}
431         @in version: query
432         @required version: True
433         @param project: project name
434         @type project: L{string}
435         @in project: query
436         @required project: True
437         @return 200: substitute test cases success.
438         @raise 404:  scenario/installer/version/project not existed
439         """
440         self.do_update('customs',
441                        'put',
442                        locators={'scenario': scenario,
443                                  'installer': None,
444                                  'version': None,
445                                  'project': None})
446
447     @swagger.operation(nickname="deleteCustomizedTestCases")
448     def delete(self, scenario):
449         """
450         @description: delete one or several customized test cases
451         @notes: delete one or some customized test cases
452             DELETE /api/v1/scenarios/<scenario_name>/customs? \
453                 installer=<installer_name>& \
454                 version=<version_name>& \
455                 project=<project_name>
456         @param body: test case(s) to be deleted
457         @type body: C{list} of L{string}
458         @in body: body
459         @param installer: installer type
460         @type installer: L{string}
461         @in installer: query
462         @required installer: True
463         @param version: version
464         @type version: L{string}
465         @in version: query
466         @required version: True
467         @param project: project name
468         @type project: L{string}
469         @in project: query
470         @required project: True
471         @return 200: delete test case(s) success.
472         @raise 404:  scenario/installer/version/project not existed
473         """
474         self.do_update('customs',
475                        'delete',
476                        locators={'scenario': scenario,
477                                  'installer': None,
478                                  'version': None,
479                                  'project': None})
480
481
482 class ScenarioProjectsHandler(GenericScenarioUpdateHandler):
483     @swagger.operation(nickname="addProjectsUnderScenario")
484     def post(self, scenario):
485         """
486         @description: add projects to scenario
487         @notes: add one or multiple projects
488             POST /api/v1/scenarios/<scenario_name>/projects? \
489                 installer=<installer_name>& \
490                 version=<version_name>
491         @param body: projects to be added
492         @type body: C{list} of L{ScenarioProject}
493         @in body: body
494         @param installer: installer type
495         @type installer: L{string}
496         @in installer: query
497         @required installer: True
498         @param version: version
499         @type version: L{string}
500         @in version: query
501         @required version: True
502         @return 200: projects are added.
503         @raise 400: bad schema
504         @raise 409: conflict, project already exists
505         @raise 404:  scenario/installer/version not existed
506         """
507         self.do_update('projects',
508                        'post',
509                        locators={'scenario': scenario,
510                                  'installer': None,
511                                  'version': None})
512
513     @swagger.operation(nickname="updateScenarioProjects")
514     def put(self, scenario):
515         """
516         @description: replace all projects
517         @notes: substitute all projects, delete existed ones with new provides
518             PUT /api/v1/scenarios/<scenario_name>/projects? \
519                 installer=<installer_name>& \
520                 version=<version_name>
521         @param body: new projects
522         @type body: C{list} of L{ScenarioProject}
523         @in body: body
524         @param installer: installer type
525         @type installer: L{string}
526         @in installer: query
527         @required installer: True
528         @param version: version
529         @type version: L{string}
530         @in version: query
531         @required version: True
532         @return 200: replace projects success.
533         @raise 400: bad schema
534         @raise 404:  scenario/installer/version not existed
535         """
536         self.do_update('projects',
537                        'put',
538                        locators={'scenario': scenario,
539                                  'installer': None,
540                                  'version': None})
541
542     @swagger.operation(nickname="deleteProjectsUnderScenario")
543     def delete(self, scenario):
544         """
545         @description: delete one or multiple projects
546         @notes: delete one or multiple projects
547             DELETE /api/v1/scenarios/<scenario_name>/projects? \
548                 installer=<installer_name>& \
549                 version=<version_name>
550         @param body: projects(names) to be deleted
551         @type body: C{list} of L{string}
552         @in body: body
553         @param installer: installer type
554         @type installer: L{string}
555         @in installer: query
556         @required installer: True
557         @param version: version
558         @type version: L{string}
559         @in version: query
560         @required version: True
561         @return 200: delete project(s) success.
562         @raise 404:  scenario/installer/version not existed
563         """
564         self.do_update('projects',
565                        'delete',
566                        locators={'scenario': scenario,
567                                  'installer': None,
568                                  'version': None})