environment_item.vue 15.4 KB
Newer Older
1
<script>
2
3
4
5
6
import Timeago from 'timeago.js';
import _ from 'underscore';
import tooltip from '~/vue_shared/directives/tooltip';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { humanize } from '~/lib/utils/text_utility';
7
import Icon from '~/vue_shared/components/icon.vue';
8
9
10
11
12
13
14
15
16
17
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue';
import RollbackComponent from './environment_rollback.vue';
import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';

/**
18
 * Environment Item Component
19
20
21
22
23
24
25
26
27
 *
 * Renders a table row for each environment.
 */
const timeagoInstance = new Timeago();

export default {
  components: {
    UserAvatarLink,
    CommitComponent,
28
    Icon,
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    ActionsComponent,
    ExternalUrlComponent,
    StopComponent,
    RollbackComponent,
    TerminalButtonComponent,
    MonitoringButtonComponent,
  },

  directives: {
    tooltip,
  },

  props: {
    model: {
      type: Object,
      required: true,
      default: () => ({}),
46
47
    },

48
49
50
51
    canCreateDeployment: {
      type: Boolean,
      required: false,
      default: false,
52
    },
53

54
55
56
57
58
59
60
61
62
    canReadEnvironment: {
      type: Boolean,
      required: false,
      default: false,
    },
  },

  computed: {
    /**
63
     * Verifies if `last_deployment` key exists in the current Environment.
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
     * This key is required to render most of the html - this method works has
     * an helper.
     *
     * @returns {Boolean}
     */
    hasLastDeploymentKey() {
      if (this.model && this.model.last_deployment && !_.isEmpty(this.model.last_deployment)) {
        return true;
      }
      return false;
    },

    /**
     * Verifies is the given environment has manual actions.
     * Used to verify if we should render them or nor.
     *
     * @returns {Boolean|Undefined}
     */
    hasManualActions() {
      return (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.manual_actions &&
        this.model.last_deployment.manual_actions.length > 0
      );
    },

91
92
93
94
95
96
97
98
99
100
    /**
     * Checkes whether the environment is protected.
     * (`is_protected` currently only set in EE)
     *
     * @returns {Boolean}
     */
    isProtected() {
      return this.model && this.model.is_protected;
    },

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    /**
     * Returns whether the environment can be stopped.
     *
     * @returns {Boolean}
     */
    canStopEnvironment() {
      return this.model && this.model.can_stop;
    },

    /**
     * Verifies if the `deployable` key is present in `last_deployment` key.
     * Used to verify whether we should or not render the rollback partial.
     *
     * @returns {Boolean|Undefined}
     */
    canRetry() {
      return (
        this.model &&
        this.hasLastDeploymentKey &&
        this.model.last_deployment &&
121
122
        this.model.last_deployment.deployable &&
        this.model.last_deployment.deployable.retry_path
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
322
323
324
325
326
327
328
329
330
331
332
333
334
      );
    },

    /**
     * Verifies if the date to be shown is present.
     *
     * @returns {Boolean|Undefined}
     */
    canShowDate() {
      return (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.deployable &&
        this.model.last_deployment.deployable !== undefined
      );
    },

    /**
     * Human readable date.
     *
     * @returns {String}
     */
    createdDate() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.deployable &&
        this.model.last_deployment.deployable.created_at
      ) {
        return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
      }
      return '';
    },

    /**
     * Returns the manual actions with the name parsed.
     *
     * @returns {Array.<Object>|Undefined}
     */
    manualActions() {
      if (this.hasManualActions) {
        return this.model.last_deployment.manual_actions.map(action => {
          const parsedAction = {
            name: humanize(action.name),
            play_path: action.play_path,
            playable: action.playable,
          };
          return parsedAction;
        });
      }
      return [];
    },

    /**
     * Builds the string used in the user image alt attribute.
     *
     * @returns {String}
     */
    userImageAltDescription() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.user &&
        this.model.last_deployment.user.username
      ) {
        return `${this.model.last_deployment.user.username}'s avatar'`;
      }
      return '';
    },

    /**
     * If provided, returns the commit tag.
     *
     * @returns {String|Undefined}
     */
    commitTag() {
      if (this.model && this.model.last_deployment && this.model.last_deployment.tag) {
        return this.model.last_deployment.tag;
      }
      return undefined;
    },

    /**
     * If provided, returns the commit ref.
     *
     * @returns {Object|Undefined}
     */
    commitRef() {
      if (this.model && this.model.last_deployment && this.model.last_deployment.ref) {
        return this.model.last_deployment.ref;
      }
      return undefined;
    },

    /**
     * If provided, returns the commit url.
     *
     * @returns {String|Undefined}
     */
    commitUrl() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.commit &&
        this.model.last_deployment.commit.commit_path
      ) {
        return this.model.last_deployment.commit.commit_path;
      }
      return undefined;
    },

    /**
     * If provided, returns the commit short sha.
     *
     * @returns {String|Undefined}
     */
    commitShortSha() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.commit &&
        this.model.last_deployment.commit.short_id
      ) {
        return this.model.last_deployment.commit.short_id;
      }
      return undefined;
    },

    /**
     * If provided, returns the commit title.
     *
     * @returns {String|Undefined}
     */
    commitTitle() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.commit &&
        this.model.last_deployment.commit.title
      ) {
        return this.model.last_deployment.commit.title;
      }
      return undefined;
    },

    /**
     * If provided, returns the commit tag.
     *
     * @returns {Object|Undefined}
     */
    commitAuthor() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.commit &&
        this.model.last_deployment.commit.author
      ) {
        return this.model.last_deployment.commit.author;
      }

      return undefined;
    },

    /**
     * Verifies if the `retry_path` key is present and returns its value.
     *
     * @returns {String|Undefined}
     */
    retryUrl() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.deployable &&
        this.model.last_deployment.deployable.retry_path
      ) {
        return this.model.last_deployment.deployable.retry_path;
      }
      return undefined;
    },

    /**
     * Verifies if the `last?` key is present and returns its value.
     *
     * @returns {Boolean|Undefined}
     */
    isLastDeployment() {
      return this.model && this.model.last_deployment && this.model.last_deployment['last?'];
    },

    /**
     * Builds the name of the builds needed to display both the name and the id.
     *
     * @returns {String}
     */
    buildName() {
      if (this.model && this.model.last_deployment && this.model.last_deployment.deployable) {
        const { deployable } = this.model.last_deployment;
        return `${deployable.name} #${deployable.id}`;
      }
      return '';
    },

    /**
     * Builds the needed string to show the internal id.
     *
     * @returns {String}
     */
    deploymentInternalId() {
      if (this.model && this.model.last_deployment && this.model.last_deployment.iid) {
        return `#${this.model.last_deployment.iid}`;
      }
      return '';
335
    },
Filipa Lacerda's avatar
Filipa Lacerda committed
336

337
338
339
340
341
342
343
344
345
346
347
    /**
     * Verifies if the user object is present under last_deployment object.
     *
     * @returns {Boolean}
     */
    deploymentHasUser() {
      return (
        this.model &&
        !_.isEmpty(this.model.last_deployment) &&
        !_.isEmpty(this.model.last_deployment.user)
      );
348
    },
Filipa Lacerda's avatar
Filipa Lacerda committed
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    /**
     * Returns the user object nested with the last_deployment object.
     * Used to render the template.
     *
     * @returns {Object}
     */
    deploymentUser() {
      if (
        this.model &&
        !_.isEmpty(this.model.last_deployment) &&
        !_.isEmpty(this.model.last_deployment.user)
      ) {
        return this.model.last_deployment.user;
      }
      return {};
365
    },
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

    /**
     * Verifies if the build name column should be rendered by verifing
     * if all the information needed is present
     * and if the environment is not a folder.
     *
     * @returns {Boolean}
     */
    shouldRenderBuildName() {
      return (
        !this.model.isFolder &&
        !_.isEmpty(this.model.last_deployment) &&
        !_.isEmpty(this.model.last_deployment.deployable)
      );
    },

    /**
     * Verifies the presence of all the keys needed to render the buil_path.
     *
     * @return {String}
     */
    buildPath() {
      if (
        this.model &&
        this.model.last_deployment &&
        this.model.last_deployment.deployable &&
        this.model.last_deployment.deployable.build_path
      ) {
        return this.model.last_deployment.deployable.build_path;
      }

      return '';
    },

    /**
     * Verifies the presence of all the keys needed to render the external_url.
     *
     * @return {String}
     */
    externalURL() {
      if (this.model && this.model.external_url) {
        return this.model.external_url;
      }

      return '';
    },

    /**
     * Verifies if deplyment internal ID should be rendered by verifing
     * if all the information needed is present
     * and if the environment is not a folder.
     *
     * @returns {Boolean}
     */
    shouldRenderDeploymentID() {
      return (
        !this.model.isFolder &&
        !_.isEmpty(this.model.last_deployment) &&
        this.model.last_deployment.iid !== undefined
      );
    },

    environmentPath() {
      if (this.model && this.model.environment_path) {
        return this.model.environment_path;
      }

      return '';
    },

    monitoringUrl() {
      if (this.model && this.model.metrics_path) {
        return this.model.metrics_path;
      }

      return '';
    },

    displayEnvironmentActions() {
      return (
        this.hasManualActions ||
        this.externalURL ||
        this.monitoringUrl ||
        this.canStopEnvironment ||
        this.canRetry
      );
    },
453
454
455
456

    folderIconName() {
      return this.model.isOpen ? 'chevron-down' : 'chevron-right';
    },
457
458
459
460
461
462
463
464
  },

  methods: {
    onClickFolder() {
      eventHub.$emit('toggleFolder', this.model);
    },
  },
};
465
466
</script>
<template>
467
  <div
468
469
470
471
    :class="{
      'js-child-row environment-child-row': model.isChildren,
      'folder-row': model.isFolder,
    }"
472
    class="gl-responsive-table-row"
Annabel Dunstone Gray's avatar
Annabel Dunstone Gray committed
473
    role="row">
Filipa Lacerda's avatar
Filipa Lacerda committed
474
    <div
475
476
477
      v-tooltip
      :title="model.name"
      class="table-section section-wrap section-15 text-truncate"
Filipa Lacerda's avatar
Filipa Lacerda committed
478
479
      role="gridcell"
    >
480
481
      <div
        v-if="!model.isFolder"
Annabel Dunstone Gray's avatar
Annabel Dunstone Gray committed
482
        class="table-mobile-header"
Filipa Lacerda's avatar
Filipa Lacerda committed
483
484
485
        role="rowheader"
      >
        {{ s__("Environments|Environment") }}
486
      </div>
487
      <span
488
        v-if="!model.isFolder"
489
490
        class="environment-name table-mobile-content">
        <a
Thong Kuah's avatar
Thong Kuah committed
491
          class="qa-environment-link"
492
493
494
495
496
          :href="environmentPath"
        >
          {{ model.name }}
        </a>
      </span>
497
498
499
      <span
        v-else
        class="folder-name"
500
501
        role="button"
        @click="onClickFolder">
502

503
504
505
506
        <icon
          :name="folderIconName"
          class="folder-icon"
        />
507

508
509
510
511
        <icon
          name="folder"
          class="folder-icon"
        />
512

513
        <span>
514
          {{ model.folderName }}
515
516
        </span>

517
        <span class="badge badge-pill">
518
          {{ model.size }}
519
        </span>
520
      </span>
521
    </div>
522

523
    <div
Clement Ho's avatar
Clement Ho committed
524
      class="table-section section-10 deployment-column d-none d-sm-none d-md-block"
525
526
      role="gridcell"
    >
527
      <span v-if="shouldRenderDeploymentID">
528
        {{ deploymentInternalId }}
529
530
531
532
      </span>

      <span v-if="!model.isFolder && deploymentHasUser">
        by
533
534
535
536
537
        <user-avatar-link
          :link-href="deploymentUser.web_url"
          :img-src="deploymentUser.avatar_url"
          :img-alt="userImageAltDescription"
          :tooltip-text="deploymentUser.username"
538
          class="js-deploy-user-container"
539
        />
540
      </span>
541
    </div>
542

Filipa Lacerda's avatar
Filipa Lacerda committed
543
    <div
Clement Ho's avatar
Clement Ho committed
544
      class="table-section section-15 d-none d-sm-none d-md-block"
Filipa Lacerda's avatar
Filipa Lacerda committed
545
546
      role="gridcell"
    >
547
548
      <a
        v-if="shouldRenderBuildName"
Filipa Lacerda's avatar
Filipa Lacerda committed
549
        :href="buildPath"
550
        class="build-link flex-truncate-parent"
Filipa Lacerda's avatar
Filipa Lacerda committed
551
552
      >
        <span class="flex-truncate-child">{{ buildName }}</span>
553
      </a>
554
    </div>
555

556
557
    <div
      v-if="!model.isFolder"
558
      class="table-section section-20"
Filipa Lacerda's avatar
Filipa Lacerda committed
559
560
      role="gridcell"
    >
561
      <div
Annabel Dunstone Gray's avatar
Annabel Dunstone Gray committed
562
        role="rowheader"
Filipa Lacerda's avatar
Filipa Lacerda committed
563
564
565
        class="table-mobile-header"
      >
        {{ s__("Environments|Commit") }}
566
      </div>
567
      <div
568
        v-if="hasLastDeploymentKey"
569
        class="js-commit-component table-mobile-content">
570
571
572
573
574
575
576
577
        <commit-component
          :tag="commitTag"
          :commit-ref="commitRef"
          :commit-url="commitUrl"
          :short-sha="commitShortSha"
          :title="commitTitle"
          :author="commitAuthor"/>
      </div>
578
      <div
579
        v-if="!hasLastDeploymentKey"
Annabel Dunstone Gray's avatar
Annabel Dunstone Gray committed
580
        class="commit-title table-mobile-content">
Filipa Lacerda's avatar
Filipa Lacerda committed
581
        {{ s__("Environments|No deployments yet") }}
582
583
      </div>
    </div>
584

585
586
    <div
      v-if="!model.isFolder"
Filipa Lacerda's avatar
Filipa Lacerda committed
587
588
589
      class="table-section section-10"
      role="gridcell"
    >
590
      <div
Annabel Dunstone Gray's avatar
Annabel Dunstone Gray committed
591
        role="rowheader"
592
        class="table-mobile-header">
Filipa Lacerda's avatar
Filipa Lacerda committed
593
        {{ s__("Environments|Updated") }}
594
      </div>
595
      <span
596
        v-if="canShowDate"
597
        class="environment-created-date-timeago table-mobile-content">
Filipa Lacerda's avatar
Filipa Lacerda committed
598
        {{ createdDate }}
599
      </span>
600
    </div>
601

602
603
604
605
606
    <div
      v-if="!model.isFolder && displayEnvironmentActions"
      class="table-section section-30 table-button-footer"
      role="gridcell">

607
      <div
608
        class="btn-group table-action-buttons"
609
610
611
612
        role="group">

        <external-url-component
          v-if="externalURL && canReadEnvironment"
613
          :external-url="externalURL"
Filipa Lacerda's avatar
Filipa Lacerda committed
614
        />
615
616
617

        <monitoring-button-component
          v-if="monitoringUrl && canReadEnvironment"
618
          :monitoring-url="monitoringUrl"
Filipa Lacerda's avatar
Filipa Lacerda committed
619
        />
620

621
        <actions-component
622
          v-if="hasManualActions && canCreateDeployment"
623
624
625
          :actions="manualActions"
        />

626
627
        <terminal-button-component
          v-if="model && model.terminal_path"
628
          :terminal-path="model.terminal_path"
Filipa Lacerda's avatar
Filipa Lacerda committed
629
        />
630
631

        <rollback-component
632
          v-if="canRetry && canCreateDeployment"
633
634
          :is-last-deployment="isLastDeployment"
          :retry-url="retryUrl"
Filipa Lacerda's avatar
Filipa Lacerda committed
635
        />
636
637
638
639
640

        <stop-component
          v-if="canStopEnvironment"
          :environment="model"
        />
641
      </div>
642
643
    </div>
  </div>
644
</template>