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:
Yann Dago 2022-12-07 19:20:45 -05:00 коммит произвёл GitHub
Родитель 45e7613992
Коммит 3648c79e02
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 68 добавлений и 39 удалений

Просмотреть файл

@ -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))