Add rollout stage to the edit_all page (#2537)
* Add rollout stage to the edit_all page - Show the rollout stage in the edit all page for all non-enterprise feature types - Use STAGE_ENT_ROLLOUT as the id for the piggybacking on the Shipping stage. * Ensure rollout stage is shown on edit all page Co-authored-by: Daniel Smith <56164590+DanielRyanSmith@users.noreply.github.com> * Use copy of forms on edit all page Co-authored-by: Daniel Smith <56164590+DanielRyanSmith@users.noreply.github.com>
This commit is contained in:
Родитель
45e7613992
Коммит
3648c79e02
|
@ -232,6 +232,7 @@ def _prep_stage_gate_info(
|
|||
ot_type = STAGE_TYPES_ORIGIN_TRIAL[fe.feature_type]
|
||||
extend_type = STAGE_TYPES_EXTEND_ORIGIN_TRIAL[fe.feature_type]
|
||||
ship_type = STAGE_TYPES_SHIPPING[fe.feature_type]
|
||||
rollout_type = STAGE_TYPES_ROLLOUT[fe.feature_type]
|
||||
|
||||
stages = Stage.query(Stage.feature_id == d['id'])
|
||||
major_stages: dict[str, Optional[Stage]] = {
|
||||
|
@ -239,7 +240,8 @@ def _prep_stage_gate_info(
|
|||
'dev_trial': None,
|
||||
'ot': None,
|
||||
'extend': None,
|
||||
'ship': None}
|
||||
'ship': None,
|
||||
'rollout': None}
|
||||
|
||||
# Write a list of stages and gates associated with the feature
|
||||
d['stages'] = stage_helpers.get_feature_stage_ids_list(d['id'])
|
||||
|
@ -258,6 +260,8 @@ def _prep_stage_gate_info(
|
|||
major_stages['extend'] = s
|
||||
elif s.stage_type == ship_type:
|
||||
major_stages['ship'] = s
|
||||
elif s.stage_type == rollout_type:
|
||||
major_stages['rollout'] = s
|
||||
|
||||
return major_stages
|
||||
|
||||
|
@ -359,10 +363,10 @@ def feature_entry_to_json_verbose(fe: FeatureEntry) -> dict[str, Any]:
|
|||
# Ship stage fields.
|
||||
d['intent_to_ship_url'] = _stage_attr(stages['ship'], 'intent_thread_url')
|
||||
d['finch_url'] = _stage_attr(stages['ship'], 'finch_url')
|
||||
d['rollout_milestone'] = _stage_attr(stages['ship'], 'rollout_milestone')
|
||||
d['rollout_platforms'] = _stage_attr(stages['ship'], 'rollout_platforms')
|
||||
d['rollout_details'] = _stage_attr(stages['ship'], 'rollout_details')
|
||||
d['enterprise_policies'] = _stage_attr(stages['ship'], 'enterprise_policies')
|
||||
d['rollout_milestone'] = _stage_attr(stages['rollout'], 'rollout_milestone')
|
||||
d['rollout_platforms'] = _stage_attr(stages['rollout'], 'rollout_platforms')
|
||||
d['rollout_details'] = _stage_attr(stages['rollout'], 'rollout_details')
|
||||
d['enterprise_policies'] = _stage_attr(stages['rollout'], 'enterprise_policies')
|
||||
|
||||
impl_status_chrome = d.pop('impl_status_chrome', None)
|
||||
standard_maturity = d.pop('standard_maturity', None)
|
||||
|
|
|
@ -68,7 +68,6 @@ class ProcessesAPITest(testing_config.CustomTestCase):
|
|||
self.feature_1.feature_type = 0
|
||||
self.feature_1.breaking_change = True
|
||||
self.feature_1.put()
|
||||
self.maxDiff = None
|
||||
with test_app.test_request_context(self.request_path):
|
||||
actual = self.handler.do_get(feature_id=self.feature_id)
|
||||
expected = processes.process_to_dict(processes.BLINK_LAUNCH_PROCESS)
|
||||
|
@ -90,7 +89,6 @@ class ProcessesAPITest(testing_config.CustomTestCase):
|
|||
self.feature_1.feature_type = 1
|
||||
self.feature_1.breaking_change = True
|
||||
self.feature_1.put()
|
||||
self.maxDiff = None
|
||||
with test_app.test_request_context(self.request_path):
|
||||
actual = self.handler.do_get(feature_id=self.feature_id)
|
||||
expected = processes.process_to_dict(processes.BLINK_FAST_TRACK_PROCESS)
|
||||
|
@ -112,7 +110,6 @@ class ProcessesAPITest(testing_config.CustomTestCase):
|
|||
self.feature_1.feature_type = 2
|
||||
self.feature_1.breaking_change = True
|
||||
self.feature_1.put()
|
||||
self.maxDiff = None
|
||||
with test_app.test_request_context(self.request_path):
|
||||
actual = self.handler.do_get(feature_id=self.feature_id)
|
||||
expected = processes.process_to_dict(processes.PSA_ONLY_PROCESS)
|
||||
|
@ -134,7 +131,6 @@ class ProcessesAPITest(testing_config.CustomTestCase):
|
|||
self.feature_1.feature_type = 3
|
||||
self.feature_1.breaking_change = True
|
||||
self.feature_1.put()
|
||||
self.maxDiff = None
|
||||
with test_app.test_request_context(self.request_path):
|
||||
actual = self.handler.do_get(feature_id=self.feature_id)
|
||||
expected = processes.process_to_dict(processes.DEPRECATION_PROCESS)
|
||||
|
@ -156,7 +152,6 @@ class ProcessesAPITest(testing_config.CustomTestCase):
|
|||
self.feature_1.feature_type = 4
|
||||
self.feature_1.breaking_change = True
|
||||
self.feature_1.put()
|
||||
self.maxDiff = None
|
||||
with test_app.test_request_context(self.request_path):
|
||||
actual = self.handler.do_get(feature_id=self.feature_id)
|
||||
expected = processes.process_to_dict(processes.ENTERPRISE_PROCESS)
|
||||
|
@ -206,7 +201,6 @@ class ProgressAPITest(testing_config.CustomTestCase):
|
|||
with test_app.test_request_context(self.request_path):
|
||||
actual = self.handler.do_get(feature_id=self.feature_id)
|
||||
|
||||
self.maxDiff = None
|
||||
self.assertEqual({
|
||||
'Code in Chromium': 'True',
|
||||
'Draft API spec': 'fake spec link',
|
||||
|
|
|
@ -3,7 +3,11 @@ import {ref} from 'lit/directives/ref.js';
|
|||
import {showToastMessage} from './utils.js';
|
||||
import './chromedash-form-table';
|
||||
import './chromedash-form-field';
|
||||
import {formatFeatureForEdit, FLAT_FORMS_BY_FEATURE_TYPE} from './form-definition';
|
||||
import {
|
||||
formatFeatureForEdit,
|
||||
FLAT_FORMS_BY_FEATURE_TYPE,
|
||||
FLAT_ENTERPRISE_PREPARE_TO_SHIP_NAME,
|
||||
FLAT_ENTERPRISE_PREPARE_TO_SHIP} from './form-definition';
|
||||
import {SHARED_STYLES} from '../sass/shared-css.js';
|
||||
import {FORM_STYLES} from '../sass/forms-css.js';
|
||||
|
||||
|
@ -31,6 +35,7 @@ export class ChromedashGuideEditallPage extends LitElement {
|
|||
super();
|
||||
this.featureId = 0;
|
||||
this.feature = {};
|
||||
this.featureForEdit = {};
|
||||
this.loading = true;
|
||||
this.appTitle = '';
|
||||
this.nextPage = '';
|
||||
|
@ -45,6 +50,7 @@ export class ChromedashGuideEditallPage extends LitElement {
|
|||
this.loading = true;
|
||||
window.csClient.getFeature(this.featureId).then((feature) => {
|
||||
this.feature = feature;
|
||||
this.featureForEdit = formatFeatureForEdit(feature);
|
||||
if (this.feature.name) {
|
||||
document.title = `${this.feature.name} - ${this.appTitle}`;
|
||||
}
|
||||
|
@ -85,10 +91,25 @@ export class ChromedashGuideEditallPage extends LitElement {
|
|||
window.location.href = `/guide/edit/${this.featureId}`;
|
||||
}
|
||||
|
||||
getForms() {
|
||||
const forms = JSON.parse(JSON.stringify(
|
||||
FLAT_FORMS_BY_FEATURE_TYPE[this.featureForEdit.feature_type]));
|
||||
|
||||
// Ensures the rollout field is shown for breaking changes.
|
||||
if (this.featureForEdit.breaking_change &&
|
||||
!forms.some(([name]) => name === FLAT_ENTERPRISE_PREPARE_TO_SHIP_NAME)) {
|
||||
forms.splice(
|
||||
forms.length - 1,
|
||||
0,
|
||||
[FLAT_ENTERPRISE_PREPARE_TO_SHIP_NAME, FLAT_ENTERPRISE_PREPARE_TO_SHIP]);
|
||||
}
|
||||
return forms;
|
||||
}
|
||||
|
||||
// get a comma-spearated list of field names
|
||||
getFormFields(featureType) {
|
||||
getFormFields() {
|
||||
let fields = [];
|
||||
FLAT_FORMS_BY_FEATURE_TYPE[featureType].map((form) => {
|
||||
this.getForms().map((form) => {
|
||||
fields = [...fields, ...form[1]];
|
||||
});
|
||||
return fields.join();
|
||||
|
@ -137,20 +158,19 @@ export class ChromedashGuideEditallPage extends LitElement {
|
|||
}
|
||||
|
||||
renderForm() {
|
||||
const formattedFeature = formatFeatureForEdit(this.feature);
|
||||
return html`
|
||||
<form name="feature_form" method="POST" action="/guide/editall/${this.featureId}">
|
||||
<input type="hidden" name="token">
|
||||
<input type="hidden" name="nextPage" value=${this.getNextPage()} >
|
||||
<input type="hidden" name="form_fields" value=${this.getFormFields(formattedFeature.feature_type)}>
|
||||
<input type="hidden" name="form_fields" value=${this.getFormFields(this.featureForEdit.feature_type)}>
|
||||
<chromedash-form-table ${ref(this.registerFormSubmitHandler)}>
|
||||
${FLAT_FORMS_BY_FEATURE_TYPE[formattedFeature.feature_type].map(([sectionName, flatFormFields]) => html`
|
||||
${this.getForms().map(([sectionName, flatFormFields]) => html`
|
||||
<h3>${sectionName}</h3>
|
||||
<section class="flat_form">
|
||||
${flatFormFields.map((field) => html`
|
||||
<chromedash-form-field
|
||||
name=${field}
|
||||
value=${formattedFeature[field]}>
|
||||
value=${this.featureForEdit[field]}>
|
||||
</chromedash-form-field>
|
||||
`)}
|
||||
</section>
|
||||
|
|
|
@ -203,11 +203,12 @@ const FLAT_SHIP_FIELDS = [
|
|||
'shipped_ios_milestone', 'shipped_webview_milestone',
|
||||
];
|
||||
|
||||
const FLAT_ENTERPRISE_PREPARE_TO_SHIP = [
|
||||
export const FLAT_ENTERPRISE_PREPARE_TO_SHIP = [
|
||||
'rollout_milestone', 'rollout_platforms', 'rollout_details',
|
||||
'enterprise_policies',
|
||||
];
|
||||
|
||||
export const FLAT_ENTERPRISE_PREPARE_TO_SHIP_NAME = 'Start feature rollout';
|
||||
|
||||
// Forms to be used on the "Edit all" page that shows a flat list of fields.
|
||||
// [[sectionName, flatFormFields]].
|
||||
|
@ -242,7 +243,7 @@ export const FLAT_FORMS_BY_FEATURE_TYPE = {
|
|||
],
|
||||
[FEATURE_TYPES.FEATURE_TYPE_ENTERPRISE_ID[0]]: [
|
||||
['Feature metadata', FLAT_METADATA_FIELDS],
|
||||
['Start feature rollout', FLAT_ENTERPRISE_PREPARE_TO_SHIP],
|
||||
[FLAT_ENTERPRISE_PREPARE_TO_SHIP_NAME, FLAT_ENTERPRISE_PREPARE_TO_SHIP],
|
||||
['Ship', FLAT_SHIP_FIELDS],
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1142,7 +1142,7 @@ export const ALL_FIELDS = {
|
|||
'rollout_milestone': {
|
||||
type: 'input',
|
||||
attrs: MILESTONE_NUMBER_FILED_ATTRS,
|
||||
required: true,
|
||||
required: false,
|
||||
label: 'Rollout milestone',
|
||||
help_text: html`
|
||||
Milestone in which rollout for this feature starts.`,
|
||||
|
@ -1151,7 +1151,7 @@ export const ALL_FIELDS = {
|
|||
'rollout_platforms': {
|
||||
type: 'multiselect',
|
||||
choices: PLATFORM_CATEGORIES,
|
||||
required: true,
|
||||
required: false,
|
||||
label: 'Rollout platforms',
|
||||
help_text: html`
|
||||
Platforms for which rollout for this feature occurs in the selected milestone.`,
|
||||
|
|
|
@ -324,6 +324,14 @@ STAGE_TYPES_SHIPPING: dict[int, Optional[int]] = {
|
|||
FEATURE_TYPE_EXISTING_ID: STAGE_FAST_SHIPPING,
|
||||
FEATURE_TYPE_CODE_CHANGE_ID: STAGE_PSA_SHIPPING,
|
||||
FEATURE_TYPE_DEPRECATION_ID: STAGE_DEP_SHIPPING,
|
||||
FEATURE_TYPE_ENTERPRISE_ID: None
|
||||
}
|
||||
# Rollout stage types for every feature type.
|
||||
STAGE_TYPES_ROLLOUT: dict[int, Optional[int]] = {
|
||||
FEATURE_TYPE_INCUBATE_ID: STAGE_ENT_ROLLOUT,
|
||||
FEATURE_TYPE_EXISTING_ID: STAGE_ENT_ROLLOUT,
|
||||
FEATURE_TYPE_CODE_CHANGE_ID: STAGE_ENT_ROLLOUT,
|
||||
FEATURE_TYPE_DEPRECATION_ID: STAGE_ENT_ROLLOUT,
|
||||
FEATURE_TYPE_ENTERPRISE_ID: STAGE_ENT_ROLLOUT
|
||||
}
|
||||
|
||||
|
@ -364,10 +372,10 @@ STAGE_TYPES_BY_FIELD_MAPPING: dict[str, dict[int, Optional[int]]] = {
|
|||
'dt_milestone_android_start': STAGE_TYPES_DEV_TRIAL,
|
||||
'dt_milestone_ios_start': STAGE_TYPES_DEV_TRIAL,
|
||||
'dt_milestone_webview_start': STAGE_TYPES_DEV_TRIAL,
|
||||
'enterprise_policies': STAGE_TYPES_SHIPPING,
|
||||
'rollout_milestone': STAGE_TYPES_SHIPPING,
|
||||
'rollout_platforms': STAGE_TYPES_SHIPPING,
|
||||
'rollout_details': STAGE_TYPES_SHIPPING
|
||||
'enterprise_policies': STAGE_TYPES_ROLLOUT,
|
||||
'rollout_milestone': STAGE_TYPES_ROLLOUT,
|
||||
'rollout_platforms': STAGE_TYPES_ROLLOUT,
|
||||
'rollout_details': STAGE_TYPES_ROLLOUT
|
||||
}
|
||||
|
||||
# Mapping of which stage types are associated with each gate type.
|
||||
|
|
|
@ -17,6 +17,8 @@ from dataclasses import asdict, dataclass
|
|||
|
||||
from internals import approval_defs
|
||||
from internals import core_enums
|
||||
from internals import core_models
|
||||
from internals import stage_helpers
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -702,18 +704,18 @@ PROGRESS_DETECTORS = {
|
|||
lambda f, _: f.impl_status_chrome == core_enums.REMOVED,
|
||||
|
||||
'Rollout milestone':
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]][0].rollout_milestone,
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]][0].rollout_milestone,
|
||||
|
||||
'Rollout platforms':
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]][0].rollout_platforms,
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]][0].rollout_platforms,
|
||||
|
||||
'Rollout details':
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]][0].rollout_details,
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]][0].rollout_details,
|
||||
|
||||
'Enterprise policies':
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]][0].enterprise_policies,
|
||||
lambda f, stages: stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]] and
|
||||
stages[core_enums.STAGE_TYPES_ROLLOUT[f.feature_type]][0].enterprise_policies,
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ class ProgressDetectorsTest(testing_config.CustomTestCase):
|
|||
name='feature one', summary='sum', category=1,
|
||||
intent_stage=core_enums.INTENT_IMPLEMENT, feature_type=0)
|
||||
self.feature_1.put()
|
||||
stage_types = [110, 120, 130, 140, 150, 151, 160]
|
||||
stage_types = [110, 120, 130, 140, 150, 151, 160, 1061]
|
||||
self.stages: list[core_models.Stage] = []
|
||||
for s_type in stage_types:
|
||||
stage = core_models.Stage(feature_id=self.feature_1.key.integer_id(),
|
||||
|
@ -281,23 +281,23 @@ class ProgressDetectorsTest(testing_config.CustomTestCase):
|
|||
def test_rollout_milestone(self):
|
||||
detector = processes.PROGRESS_DETECTORS['Rollout milestone']
|
||||
self.assertFalse(detector(self.feature_1, self.stages_dict))
|
||||
self.stages_dict[160][0].rollout_milestone = 99
|
||||
self.stages_dict[1061][0].rollout_milestone = 99
|
||||
self.assertTrue(detector(self.feature_1, self.stages_dict))
|
||||
|
||||
def test_rollout_platforms(self):
|
||||
detector = processes.PROGRESS_DETECTORS['Rollout platforms']
|
||||
self.assertFalse(detector(self.feature_1, self.stages_dict))
|
||||
self.stages_dict[160][0].rollout_platforms = ['iOS', 'Android']
|
||||
self.stages_dict[1061][0].rollout_platforms = ['iOS', 'Android']
|
||||
self.assertTrue(detector(self.feature_1, self.stages_dict))
|
||||
|
||||
def test_rollout_details(self):
|
||||
detector = processes.PROGRESS_DETECTORS['Rollout details']
|
||||
self.assertFalse(detector(self.feature_1, self.stages_dict))
|
||||
self.stages_dict[160][0].rollout_details = 'Details'
|
||||
self.stages_dict[1061][0].rollout_details = 'Details'
|
||||
self.assertTrue(detector(self.feature_1, self.stages_dict))
|
||||
|
||||
def test_enterprise_policies(self):
|
||||
detector = processes.PROGRESS_DETECTORS['Enterprise policies']
|
||||
self.assertFalse(detector(self.feature_1, self.stages_dict))
|
||||
self.stages_dict[160][0].enterprise_policies = ['Policy1', 'Policy2']
|
||||
self.stages_dict[1061][0].enterprise_policies = ['Policy1', 'Policy2']
|
||||
self.assertTrue(detector(self.feature_1, self.stages_dict))
|
||||
|
|
Загрузка…
Ссылка в новой задаче