1/**
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { Component, OnInit, ViewChild } from '@angular/core';
17import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material';
18import { animate, state, style, transition, trigger } from "@angular/animations";
19
20import { AppService } from '../../appservice';
21import { FilterComponent } from '../../shared/filter/filter.component';
22import { FilterItem } from '../../model/filter_item';
23import { MenuBaseClass } from '../menu_base';
24import { Schedule, ScheduleSuspendResponse } from '../../model/schedule';
25import { ScheduleService } from './schedule.service';
26
27
28/** Component that handles schedule menu. */
29@Component({
30  selector: 'app-schedule',
31  templateUrl: './schedule.component.html',
32  providers: [ ScheduleService ],
33  styleUrls: ['./schedule.component.scss'],
34  animations: [
35    trigger('detailExpand', [
36      state('void', style({height: '0px', minHeight: '0', visibility: 'hidden'})),
37      state('*', style({height: '*', visibility: 'visible'})),
38      transition('void <=> *', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
39    ]),
40  ],
41})
42export class ScheduleComponent extends MenuBaseClass implements OnInit {
43  columnTitles = [
44    '_index',
45    'test_name',
46    'device',
47    'manifest_branch',
48    'build_target',
49    'gsi_branch',
50    'gsi_build_target',
51    'test_branch',
52    'test_build_target',
53    'period',
54    'status',
55    'timestamp',
56  ];
57  dataSource = new MatTableDataSource<Schedule>();
58  pageEvent: PageEvent;
59  appliedFilters: FilterItem[];
60
61  @ViewChild(FilterComponent) filterComponent: FilterComponent;
62
63  constructor(private scheduleService: ScheduleService,
64              appService: AppService,
65              snackBar: MatSnackBar) {
66    super(appService, snackBar);
67  }
68
69  ngOnInit(): void {
70    this.filterComponent.setSelectorList(Schedule);
71    this.getCount();
72    this.getSchedules(this.pageSize, this.pageSize * this.pageIndex);
73  }
74
75  /** Gets a total count of schedules. */
76  getCount(observer = this.getDefaultCountObservable()) {
77    const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : '';
78    this.scheduleService.getCount(filterJSON).subscribe(observer);
79  }
80
81  /** Gets schedules.
82   * @param size A number, at most this many results will be returned.
83   * @param offset A Number of results to skip.
84   */
85  getSchedules(size = 0, offset = 0) {
86    this.loading = true;
87    const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : '';
88    this.scheduleService.getSchedules(size, offset, filterJSON, '', '')
89      .subscribe(
90        (response) => {
91          this.loading = false;
92          if (this.count >= 0) {
93            let length = 0;
94            if (response.schedules) {
95              length = response.schedules.length;
96            }
97            const total = length + offset;
98            if (response.has_next) {
99              if (length !== this.pageSize) {
100                this.showSnackbar('Received unexpected number of entities.');
101              } else if (this.count <= total) {
102                this.getCount();
103              }
104            } else {
105              if (this.count !== total) {
106                if (length !== this.count) {
107                  this.getCount();
108                } else if (this.count > total) {
109                  const countObservable = this.getDefaultCountObservable([
110                    () => {
111                      this.pageIndex = Math.floor(this.count / this.pageSize);
112                      this.getSchedules(this.pageSize, this.pageSize * this.pageIndex);
113                    }
114                  ]);
115                  this.getCount(countObservable);
116                }
117              }
118            }
119          }
120          this.dataSource.data = response.schedules;
121        },
122        (error) => this.showSnackbar(`[${error.status}] ${error.name}`)
123      );
124  }
125
126  /** Toggles a schedule from suspend to resume, or vice versa. */
127  suspendSchedule(schedules: ScheduleSuspendResponse[]) {
128    this.scheduleService.suspendSchedule(schedules)
129      .subscribe(
130        (response) => {
131          if (response.schedules) {
132            let self = this;
133            response.schedules.forEach(function(schedule) {
134                const original = self.dataSource.data.filter(x => x.urlsafe_key === schedule.urlsafe_key);
135                if (original) {
136                  original[0].suspended = schedule.suspend;
137                }
138              })
139          }
140        },
141        (error) => this.showSnackbar(`[${error.status}] ${error.name}`)
142      );
143  }
144
145  /** Hooks a page event and handles properly. */
146  onPageEvent(event: PageEvent) {
147    this.pageSize = event.pageSize;
148    this.pageIndex = event.pageIndex;
149    this.getSchedules(this.pageSize, this.pageSize * this.pageIndex);
150    return event;
151  }
152
153  /** Applies a filter and get entities with it. */
154  applyFilters(filters) {
155    this.pageIndex = 0;
156    this.appliedFilters = filters;
157    this.getCount();
158    this.getSchedules(this.pageSize, this.pageSize * this.pageIndex);
159  }
160}
161