Merge "Refactor the OTA configuration page using several components."

This commit is contained in:
Treehugger Robot
2021-07-19 18:31:32 +00:00
committed by Gerrit Code Review
7 changed files with 387 additions and 253 deletions

View File

@@ -0,0 +1,120 @@
<template>
<ul>
<h3>Build Library</h3>
<UploadFile @file-uploaded="fetchTargetList" />
<BuildTable
v-if="targetDetails.length>0"
:builds="targetDetails"
/>
<li
v-for="targetDetail in targetDetails"
:key="targetDetail.file_name"
>
<div>
<h3> Build File Name: {{ targetDetail.file_name }} </h3>
<strong> Uploaded time: </strong> {{ formDate(targetDetail.time) }}
<br>
<strong> Build ID: </strong> {{ targetDetail.build_id }}
<br>
<strong> Build Version: </strong> {{ targetDetail.build_version }}
<br>
<strong> Build Flavor: </strong> {{ targetDetail.build_flavor }}
<br>
<v-btn
:disabled="!isIncremental"
@click="selectIncremental(targetDetail.path)"
>
Select as Incremental File
</v-btn>
&emsp;
<v-btn @click="selectTarget(targetDetail.path)">
Select as Target File
</v-btn>
</div>
<v-divider class="my-5" />
</li>
<v-btn @click="updateBuildLib">
Refresh the build Library (use with cautions)
</v-btn>
</ul>
</template>
<script>
import UploadFile from '@/components/UploadFile.vue'
import BuildTable from '@/components/BuildTable.vue'
import ApiService from '@/services/ApiService.js'
import FormDate from '@/services/FormDate.js'
export default {
components: {
UploadFile,
BuildTable
},
props: {
isIncremental: {
type: Boolean,
required: true
}
},
data() {
return {
incrementalSource: '',
targetBuild: '',
targetDetails: []
}
},
mounted () {
this.fetchTargetList()
},
methods: {
/**
* Fetch the build list from backend.
*/
async fetchTargetList() {
try {
let response = await ApiService.getFileList('')
this.targetDetails = response.data
this.$emit('update:targetDetails', response.data)
} catch (err) {
alert(
"Cannot fetch Android Builds list from the backend, for the following reasons:"
+ err
)
}
},
/**
* Let the backend reload the builds from disk. This will overwrite the
* original upload time.
*/
async updateBuildLib() {
try {
let response = await ApiService.getFileList('/target')
this.targetDetails = response.data
this.$emit('update:targetDetails', response.data)
} catch (err) {
alert(
"Cannot fetch Android Builds list from the backend, for the following reasons: "
+ err
)
}
},
selectTarget(path) {
this.targetBuild = path
this.$emit('update:targetBuild', path)
},
selectIncremental(path) {
this.incrementalSource = path
this.$emit('update:incrementalSource', path)
},
formDate(unixTime) {
return FormDate.formDate(unixTime)
}
},
}
</script>
<style scoped>
ul > li {
list-style: none
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<form @submit.prevent="sendForm">
<FileSelect
v-if="input.isIncremental"
v-model="input.incremental"
label="Select the source file"
:options="targetDetails"
/>
<FileSelect
v-model="input.target"
label="Select a target file"
:options="targetDetails"
/>
<v-row>
<v-col
cols="12"
md="4"
align="center"
>
<BaseCheckbox
v-model="input.verbose"
:label="'Verbose'"
/>
</v-col>
<v-col
cols="12"
md="4"
align="center"
>
<BaseCheckbox
v-model="input.isIncremental"
:label="'Incremental'"
/>
</v-col>
<v-col
cols="12"
md="4"
align="center"
>
<BaseCheckbox
v-model="input.isPartial"
:label="'Partial'"
/>
</v-col>
</v-row>
<div>
<PartialCheckbox
v-if="input.isPartial"
v-model="input.partial"
:labels="updatablePartitions"
/>
</div>
<v-divider class="my-5" />
<BaseInput
v-model="input.extra"
:label="'Extra Configurations'"
/>
<v-divider class="my-5" />
<v-btn
block
type="submit"
>
Submit
</v-btn>
</form>
</template>
<script>
import BaseInput from '@/components/BaseInput.vue'
import BaseCheckbox from '@/components/BaseCheckbox.vue'
import FileSelect from '@/components/FileSelect.vue'
import PartialCheckbox from '@/components/PartialCheckbox.vue'
import { OTAConfiguration } from '@/services/JobSubmission.js'
export default {
components:{
BaseInput,
BaseCheckbox,
FileSelect,
PartialCheckbox,
},
props: {
targetDetails: {
type: Array,
default: () => []
},
incrementalSource: {
type: String,
default: ''
},
targetBuild: {
type: String,
default: ''
}
},
data () {
return {
input: new OTAConfiguration()
}
},
computed: {
/**
* Return the partition list of the selected target build.
* @return Array<String>
*/
updatablePartitions() {
if (!this.input.target) return []
let target = this.targetDetails.filter(
(d) => d.path === this.input.target
)
return target[0].partitions
},
checkIncremental() {
return this.input.isIncremental
}
},
watch: {
incrementalSource: {
handler: function () {
this.input.isIncremental = true
this.input.incremental = this.incrementalSource
}
},
targetBuild: {
handler: function () {
this.input.target = this.targetBuild
}
},
checkIncremental: {
handler: function () {
this.$emit('update:isIncremental', this.checkIncremental)
}
}
},
methods: {
/**
* Send the configuration to the backend.
*/
async sendForm() {
try {
let response_message = await this.input.sendForm()
alert(response_message)
} catch (err) {
alert('Job cannot be started properly for the following reasons: ' + err)
}
this.input = new OTAInput()
}
}
}
</script>

View File

@@ -21,7 +21,7 @@
<input <input
type="checkbox" type="checkbox"
:value="label" :value="label"
:checked="modelValue.get(label)" :checked="partitionSelected.get(label)"
@change="updateSelected($event.target.value)" @change="updateSelected($event.target.value)"
> >
{{ label }} {{ label }}
@@ -38,34 +38,48 @@ export default {
default: new Array(), default: new Array(),
}, },
modelValue: { modelValue: {
type: Map, type: String,
default: new Map(), required: true
}, },
}, },
data() { data() {
return { return {
selectAll: 1, selectAll: 1,
selectAllText: ['Select All', 'Unselect All'], selectAllText: ['Select All', 'Unselect All'],
partitionSelected: new Map()
}
},
watch: {
partitionSelected: {
handler: function() {
let list = ''
for (let [key, value] of this.partitionSelected) {
if (value) {
list += key + ' '
}
}
this.$emit('update:modelValue', list)
},
deep: true
} }
}, },
mounted() { mounted() {
// Set the default value to be true once mounted // Set the default value to be true once mounted
for (let key of this.labels) { for (let key of this.labels) {
this.modelValue.set(key, true) this.partitionSelected.set(key, true)
} }
}, },
methods: { methods: {
updateSelected(newSelect) { updateSelected(newSelect) {
this.modelValue.set(newSelect, !this.modelValue.get(newSelect)) this.partitionSelected.set(newSelect, !this.partitionSelected.get(newSelect))
this.$emit('update:modelValue', this.modelValue)
}, },
revertAllSelection() { revertAllSelection() {
this.selectAll = 1 - this.selectAll this.selectAll = 1 - this.selectAll
for (let key of this.modelValue.keys()) { for (let key of this.partitionSelected.keys()) {
this.modelValue.set(key, Boolean(this.selectAll)) this.partitionSelected.set(key, Boolean(this.selectAll))
} }
}, },
}, }
} }
</script> </script>

View File

@@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
import JobList from '@/views/JobList.vue' import JobList from '@/views/JobList.vue'
import JobDetails from '@/views/JobDetails.vue' import JobDetails from '@/views/JobDetails.vue'
import About from '@/views/About.vue' import About from '@/views/About.vue'
import SimpleForm from '@/views/SimpleForm.vue' import JobConfigure from '@/views/JobConfigure.vue'
const routes = [ const routes = [
{ {
@@ -24,7 +24,7 @@ const routes = [
{ {
path: '/create', path: '/create',
name: 'Create', name: 'Create',
component: SimpleForm component: JobConfigure
} }
] ]

View File

@@ -0,0 +1,37 @@
/**
* @fileoverview Class OTAInput is used to configure and create a process in
* the backend to to start OTA package generation.
* @package vue-uuid
* @package ApiServices
*/
import { uuid } from 'vue-uuid'
import ApiServices from './ApiService.js'
export class OTAConfiguration {
/**
* Initialize the input for the api /run/<id>
*/
constructor() {
this.verbose = false,
this.target = '',
this.output = 'output/' + String(this.id) + '.zip',
this.incremental = '',
this.isIncremental = false,
this.partial = '',
this.isPartial = false,
this.extra = '',
this.id = uuid.v1()
}
/**
* Start the generation process, will throw an error if not succeed
*/
async sendForm() {
try {
let response = await ApiServices.postInput(JSON.stringify(this), this.id)
return response.data
} catch (err) {
throw err
}
}
}

View File

@@ -0,0 +1,55 @@
<template>
<v-row>
<v-col
cols="12"
md="6"
>
<OTAOptions
:targetDetails="targetDetails"
:incrementalSource="incrementalSource"
:targetBuild="targetBuild"
@update:isIncremental="isIncremental = $event"
/>
</v-col>
<v-divider vertical />
<v-col
cols="12"
md="6"
class="library"
>
<BuildLibrary
:isIncremental="isIncremental"
@update:incrementalSource="incrementalSource = $event"
@update:targetBuild="targetBuild = $event"
@update:targetDetails="targetDetails = $event"
/>
</v-col>
</v-row>
</template>
<script>
import OTAOptions from '@/components/OTAOptions.vue'
import BuildLibrary from '@/components/BuildLibrary.vue'
export default {
components: {
OTAOptions,
BuildLibrary
},
data() {
return {
incrementalSource: '',
targetBuild: '',
targetDetails: [],
isIncremental: false
}
},
}
</script>
<style scoped>
.library {
overflow: scroll;
height:calc(100vh - 80px);
}
</style>>

View File

@@ -1,242 +0,0 @@
<template>
<v-row>
<v-col
cols="12"
md="6"
>
<form @submit.prevent="sendForm">
<FileSelect
v-if="input.isIncremental"
v-model="input.incremental"
label="Select the source file"
:options="targetDetails"
/>
<FileSelect
v-model="input.target"
label="Select a target file"
:options="targetDetails"
/>
<v-row>
<v-col
cols="12"
md="4"
align="center"
>
<BaseCheckbox
v-model="input.verbose"
:label="'Verbose'"
/>
</v-col>
<v-col
cols="12"
md="4"
align="center"
>
<BaseCheckbox
v-model="input.isIncremental"
:label="'Incremental'"
/>
</v-col>
<v-col
cols="12"
md="4"
align="center"
>
<BaseCheckbox
v-model="input.isPartial"
:label="'Partial'"
/>
</v-col>
</v-row>
<div>
<PartialCheckbox
v-if="input.isPartial"
v-model="partitionInclude"
:labels="updatePartitions"
/>
</div>
<v-divider class="my-5" />
<BaseInput
v-model="input.extra"
:label="'Extra Configurations'"
/>
<v-divider class="my-5" />
<v-btn
block
type="submit"
>
Submit
</v-btn>
</form>
</v-col>
<v-divider vertical />
<v-col
cols="12"
md="6"
>
<ul>
<h3>Build Library</h3>
<UploadFile @file-uploaded="fetchTargetList" />
<BuildTable
v-if="targetDetails.length>0"
:builds="targetDetails"
/>
<li
v-for="targetDetail in targetDetails"
:key="targetDetail.file_name"
>
<div>
<h3> Build File Name: {{ targetDetail.file_name }} </h3>
<strong> Uploaded time: </strong> {{ formDate(targetDetail.time) }}
<br>
<strong> Build ID: </strong> {{ targetDetail.build_id }}
<br>
<strong> Build Version: </strong> {{ targetDetail.build_version }}
<br>
<strong> Build Flavor: </strong> {{ targetDetail.build_flavor }}
<br>
<v-btn
:disabled="!input.isIncremental"
@click="selectIncremental(targetDetail.path)"
>
Select as Incremental File
</v-btn>
&emsp;
<v-btn @click="selectTarget(targetDetail.path)">
Select as Target File
</v-btn>
</div>
<v-divider class="my-5" />
</li>
<v-btn @click="updateBuildLib">
Refresh the build Library (use with cautions)
</v-btn>
</ul>
</v-col>
</v-row>
</template>
<script>
import BaseInput from '@/components/BaseInput.vue'
import BaseCheckbox from '@/components/BaseCheckbox.vue'
import FileSelect from '@/components/FileSelect.vue'
import ApiService from '../services/ApiService.js'
import UploadFile from '@/components/UploadFile.vue'
import PartialCheckbox from '@/components/PartialCheckbox.vue'
import FormDate from '../services/FormDate.js'
import BuildTable from '@/components/BuildTable.vue'
import { uuid } from 'vue-uuid'
export default {
components: {
BaseInput,
BaseCheckbox,
UploadFile,
FileSelect,
PartialCheckbox,
BuildTable
},
data() {
return {
id: 0,
input: {},
inputs: [],
response_message: '',
targetDetails: [],
partitionInclude: new Map(),
}
},
computed: {
updatePartitions() {
if (!this.input.target) return []
let target = this.targetDetails.filter(
(d) => d.path === this.input.target
)
return target[0].partitions
},
partitionList() {
let list = ''
for (let [key, value] of this.partitionInclude) {
if (value) {
list += key + ' '
}
}
return list
},
},
watch: {
partitionList: {
handler: function () {
this.input.partial = this.partitionList
},
},
},
created() {
this.resetInput()
this.fetchTargetList()
this.updateUUID()
},
methods: {
resetInput() {
this.input = {
verbose: false,
target: '',
output: 'output/',
incremental: '',
isIncremental: false,
partial: '',
isPartial: false,
extra: '',
},
this.partitionInclude = new Map()
},
async sendForm(e) {
try {
let response = await ApiService.postInput(this.input, this.id)
this.response_message = response.data
alert(this.response_message)
} catch (err) {
alert('Job cannot be started properly, please check.')
console.log(err)
}
this.resetInput()
this.updateUUID()
},
async fetchTargetList() {
try {
let response = await ApiService.getFileList('')
this.targetDetails = response.data
} catch (err) {
console.log('Fetch Error', err)
}
},
updateUUID() {
this.id = uuid.v1()
this.input.output += String(this.id) + '.zip'
},
formDate(unixTime) {
return FormDate.formDate(unixTime)
},
selectTarget(path) {
this.input.target = path
},
selectIncremental(path) {
this.input.incremental = path
},
async updateBuildLib() {
try {
let response = await ApiService.getFileList('/target')
this.targetDetails = response.data
} catch (err) {
console.log('Fetch Error', err)
}
},
},
}
</script>
<style scoped>
ul > li {
list-style: none
}
</style>