Skip to content

Commit

Permalink
Merge branch 'GoogleChrome:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafacco7 authored Sep 16, 2024
2 parents cbbbd10 + d821118 commit 3a0b25a
Show file tree
Hide file tree
Showing 400 changed files with 32,340 additions and 12,538 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
node-version: 18
- name: Run your tests
timeout-minutes: 30
run: npm run pwtests-shutdown --workspace=playwright; npm run test --workspace=playwright
run: npm run pwtests-shutdown; npm run pwtests
- uses: actions/upload-artifact@v4
if: failure()
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
persist-credentials: false

- name: "Run analysis"
uses: ossf/scorecard-action@v2.3.3
uses: ossf/scorecard-action@v2.4.0
with:
results_file: results.sarif
results_format: sarif
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ node_modules
# Generated files
static/css/
static/dist/
static/js/

# OS files
*.DS_Store
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ your change of the test files.

To run the Playwright visual tests (aka end-to-end tests), the command to use is:
```bash
npm run pwtests --workspace=playwright
npm run pwtests
```

If there are errors, they will be displayed in the console.
If you need to update any of the screenshot images, you will see differences in
the `packages/playwright/test-results` directory, and if they look correct,
then you can update _all_ images for all tests with:
```bash
npm run pwtests-update --workspace=playwright
npm run pwtests-update
```

The updated images are also added to the __screenshots__ directory. Images that
Expand Down
9 changes: 3 additions & 6 deletions api/accounts_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
from chromestatus_openapi.models import AccountResponse

from google.cloud import ndb # type: ignore

from framework import basehandlers
from framework import permissions
from framework import basehandlers, permissions
from internals import user_models


Expand All @@ -43,7 +40,7 @@ def do_post(self, **kwargs):
is_admin = self.get_bool_param('isAdmin')
is_site_editor = self.get_bool_param('isSiteEditor')
user = self.create_account(email, is_admin, is_site_editor)
response_json = user_to_json_dict(user)
response_json = AccountResponse(is_admin=user.is_admin, is_site_editor=user.is_site_editor, email=user.email, id=user.key.integer_id()).to_dict()
return response_json

def create_account(self, email, is_admin, is_site_editor):
Expand Down
2 changes: 2 additions & 0 deletions api/api_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
('availability_expectation', 'str'),
('blink_components', 'split_str'),
('enterprise_impact', 'int'),
('shipping_year', 'int'),
('breaking_change', 'bool'),
('bug_url', 'link'),
('category', 'int'),
Expand Down Expand Up @@ -106,6 +107,7 @@
('origin_trial_feedback_url', 'link'),
('origin_trial_id', 'str'),
('ot_approval_buganizer_component', 'int'),
('ot_approval_buganizer_custom_field_id', 'int'),
('ot_approval_criteria_url', 'str'),
('ot_approval_group_email', 'str'),
('ot_chromium_trial_name', 'str'),
Expand Down
79 changes: 44 additions & 35 deletions api/comments_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,46 @@

from typing import Any

from framework import basehandlers
from framework import permissions
from chromestatus_openapi.models import (
Activity as ActivityModel,
)
from chromestatus_openapi.models import (
Amendment as AmendmentModel,
)
from chromestatus_openapi.models import (
CommentsRequest,
GetCommentsResponse,
PatchCommentRequest,
SuccessMessage,
)

from framework import basehandlers, permissions
from internals import approval_defs, notifier, notifier_helpers, slo
from internals.review_models import Activity, Amendment, Gate
from internals import approval_defs
from internals import notifier
from internals import notifier_helpers
from internals import slo


def amendment_to_json_dict(amendment: Amendment) -> dict[str, Any]:
return {
'field_name': amendment.field_name,
'old_value': amendment.old_value.strip('[]'),
'new_value': amendment.new_value.strip('[]'),
}
def amendment_to_OAM(amendment: Amendment) -> AmendmentModel:
return AmendmentModel(
field_name = amendment.field_name,
old_value = amendment.old_value.strip('[]'),
new_value = amendment.new_value.strip('[]'),
)


def activity_to_json_dict(comment: Activity) -> dict[str, Any]:
def activity_to_OAM(comment: Activity) -> ActivityModel:
amendments_json = [
amendment_to_json_dict(amnd) for amnd in comment.amendments
amendment_to_OAM(amnd) for amnd in comment.amendments
if amnd.old_value != 'None' or amnd.new_value != '[]']
return {
'comment_id': comment.key.id(),
'feature_id': comment.feature_id,
'gate_id': comment.gate_id,
'created': str(comment.created), # YYYY-MM-DD HH:MM:SS.SSS
'author': comment.author,
'content': comment.content,
'deleted_by': comment.deleted_by,
'amendments': amendments_json,
}
return ActivityModel(
comment_id = comment.key.id(),
feature_id = comment.feature_id,
gate_id = comment.gate_id,
created = str(comment.created), # YYYY-MM-DD HH:MM:SS.SSS
author = comment.author,
content = comment.content,
deleted_by = comment.deleted_by,
amendments = amendments_json,
)


class CommentsAPI(basehandlers.APIHandler):
Expand All @@ -71,19 +80,20 @@ def do_get(self, **kwargs) -> dict[str, list[dict[str, Any]]]:
comments = list(filter(
lambda c: self._should_show_comment(c, user_email, is_admin), comments))

dicts = [activity_to_json_dict(c) for c in comments]
return {'comments': dicts}
dicts = [activity_to_OAM(c) for c in comments]
return GetCommentsResponse(comments=dicts).to_dict()

def do_post(self, **kwargs) -> dict[str, str]:
"""Add a review comment and possibly set a approval value."""
feature_id = kwargs['feature_id']
gate_id = kwargs.get('gate_id', None)
feature = self.get_specified_feature(feature_id=feature_id)
user = self.get_current_user(required=True)
post_to_thread_type = self.get_param(
'postToThreadType', required=False)

comment_content = self.get_param('comment', required=False)
comment_request = CommentsRequest.from_dict(self.request.json)
comment_content = comment_request.comment
post_to_thread_type = comment_request.post_to_thread_type

if comment_content:
can_comment = (permissions.can_comment(user) or
permissions.can_edit_feature(user, feature_id))
Expand Down Expand Up @@ -114,22 +124,21 @@ def do_post(self, **kwargs) -> dict[str, str]:
feature, gate_id, post_to_thread_type, user.email(), comment_content)

# Callers don't use the JSON response for this API call.
return {'message': 'Done'}
return SuccessMessage(message='Done').to_dict()

def do_patch(self, **kwargs) -> dict[str, str]:
comment_id = self.get_param('commentId', required=True)
comment: Activity = Activity.get_by_id(comment_id)
patch_request = PatchCommentRequest.from_dict(self.request.json)
comment: Activity = Activity.get_by_id(patch_request.comment_id)

user = self.get_current_user(required=True)
if not permissions.can_admin_site(user) and (
comment and user.email() != comment.author):
self.abort(403, msg='User does not have comment edit permissions')

is_undelete = self.get_param('isUndelete', required=True)
if is_undelete:
if patch_request.is_undelete:
comment.deleted_by = None
else:
comment.deleted_by = user.email()
comment.put()

return {'message': 'Done'}
return SuccessMessage(message='Done').to_dict()
22 changes: 13 additions & 9 deletions api/comments_api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import datetime
import testing_config # Must be imported before the module under test.
from unittest import mock

import flask
from unittest import mock
import werkzeug.exceptions # Flask HTTP stuff.
from chromestatus_openapi.models import (
Amendment as AmendmentModel,
Activity as ActivityModel,
)

import testing_config # Must be imported before the module under test.
from api import comments_api
from internals.core_models import FeatureEntry
from internals.review_models import Activity, Amendment, Gate, Vote
Expand All @@ -34,16 +37,16 @@ class CommentsConvertersTest(testing_config.CustomTestCase):
def test_amendment_to_json_dict(self):
amnd = Amendment(
field_name='summary', old_value='foo', new_value='bar')
expected = dict(field_name='summary', old_value='foo', new_value='bar')
actual = comments_api.amendment_to_json_dict(amnd)
expected = AmendmentModel(field_name='summary', old_value='foo', new_value='bar')
actual = comments_api.amendment_to_OAM(amnd)
self.assertEqual(expected, actual)

def test_amendment_to_json_dict__arrays(self):
"""Arrays are shown without the brackets."""
amnd = Amendment(
field_name='summary', old_value='[1, 2]', new_value='[1, 2, 3]')
expected = dict(field_name='summary', old_value='1, 2', new_value='1, 2, 3')
actual = comments_api.amendment_to_json_dict(amnd)
expected = AmendmentModel(field_name='summary', old_value='1, 2', new_value='1, 2, 3')
actual = comments_api.amendment_to_OAM(amnd)
self.assertEqual(expected, actual)

def test_activity_to_json_dict(self):
Expand All @@ -56,8 +59,8 @@ def test_activity_to_json_dict(self):
id=1, feature_id=123, gate_id=456, created=created,
author='[email protected]', content='hello',
amendments=[amnd_1, amnd_2])
actual = comments_api.activity_to_json_dict(act)
expected = {
actual = comments_api.activity_to_OAM(act)
expected_dict = {
'comment_id': 1,
'feature_id': 123,
'gate_id': 456,
Expand All @@ -71,6 +74,7 @@ def test_activity_to_json_dict(self):
'new_value': 'bar',
}],
}
expected = ActivityModel.from_dict(expected_dict)
self.assertEqual(expected, actual)


Expand Down
9 changes: 6 additions & 3 deletions api/component_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from chromestatus_openapi.models import ComponentUsersRequest

from framework import basehandlers
from framework import permissions
from internals import user_models
Expand Down Expand Up @@ -47,16 +49,17 @@ def do_get(self, **kwargs):

@permissions.require_admin_site
def do_put(self, **kwargs) -> tuple[dict, int]:
params = self.request.get_json(force=True)
component_users_request = ComponentUsersRequest.from_dict(self.request.get_json(force=True))
self.__update_subscribers_list(True, user_id=kwargs.get('user_id', None),
blink_component_id=kwargs.get('component_id', None),
primary=params.get('owner'))
primary=component_users_request.owner)
return {}, 200

@permissions.require_admin_site
def do_delete(self, **kwargs) -> tuple[dict, int]:
params = self.request.get_json(force=True)
component_users_request = ComponentUsersRequest.from_dict(params)
self.__update_subscribers_list(False, user_id=kwargs.get('user_id', None),
blink_component_id=kwargs.get('component_id', None),
primary=params.get('owner'))
primary=component_users_request.owner)
return {}, 200
8 changes: 8 additions & 0 deletions api/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ def _prep_stage_info(
(stage_info['all_stages'][ot_stage_indexes[ot_id]]['extensions']
.append(extension))
stage_info['all_stages'].sort(key=lambda s: (s['stage_type'], s['created']))
# Sort all extensions in order of creation as well.
for stage in stage_info['all_stages']:
stage['extensions'].sort(key=lambda s: (s['created']))
return stage_info


Expand Down Expand Up @@ -201,6 +204,8 @@ def stage_to_json_dict(
'origin_trial_feedback_url': stage.origin_trial_feedback_url,
'ot_action_requested': stage.ot_action_requested,
'ot_approval_buganizer_component': stage.ot_approval_buganizer_component,
'ot_approval_buganizer_custom_field_id': (
stage.ot_approval_buganizer_custom_field_id),
'ot_approval_criteria_url': stage.ot_approval_criteria_url,
'ot_approval_group_email': stage.ot_approval_group_email,
'ot_chromium_trial_name': stage.ot_chromium_trial_name,
Expand Down Expand Up @@ -239,6 +244,8 @@ def stage_to_json_dict(

if stage.ot_activation_date:
d['ot_activation_date'] = str(stage.ot_activation_date)
if stage.ot_setup_status:
d['ot_setup_status'] = stage.ot_setup_status

return d

Expand Down Expand Up @@ -374,6 +381,7 @@ def feature_entry_to_json_verbose(
'first_enterprise_notification_milestone': fe.first_enterprise_notification_milestone,
'enterprise_impact': fe.enterprise_impact,
'breaking_change': fe.breaking_change,
'shipping_year': fe.shipping_year,
'flag_name': fe.flag_name,
'finch_name': fe.finch_name,
'non_finch_justification': fe.non_finch_justification,
Expand Down
4 changes: 2 additions & 2 deletions api/converters_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def setUp(self):
updater_email='[email protected]', category=1,
owner_emails=['[email protected]'], feature_type=0,
editor_emails=['[email protected]', '[email protected]'],
impl_status_chrome=5, blink_components=['Blink'],
impl_status_chrome=5, blink_components=['Blink'], shipping_year=2024,
spec_link='https://example.com/spec',
sample_links=['https://example.com/samples'],
screenshot_links=['https://example.com/screenshot'],
Expand Down Expand Up @@ -293,6 +293,7 @@ def test_feature_entry_to_json_verbose__normal(self):
'unlisted': False,
'api_spec': False,
'enterprise_impact': ENTERPRISE_IMPACT_NONE,
'shipping_year': 2024,
'breaking_change': False,
'is_released': True,
'category': 'Web Components',
Expand Down Expand Up @@ -369,7 +370,6 @@ def test_feature_entry_to_json_verbose__normal(self):
'non_oss_deps': None,
'ongoing_constraints': None,
'owner_emails': ['[email protected]'],
'owner_emails': ['[email protected]'],
'safari_views': 1,
'search_tags': [],
'security_risks': None,
Expand Down
14 changes: 9 additions & 5 deletions api/cues_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from chromestatus_openapi.models import (DismissCueRequest)

import logging

from framework import basehandlers
Expand All @@ -33,12 +35,14 @@ class CuesAPI(basehandlers.APIHandler):

def do_post(self, **kwargs):
"""Dismisses a cue card for the signed in user."""
cue = self.get_param('cue', allowed=ALLOWED_CUES)
unused_user = self.get_current_user(required=True)

user_models.UserPref.dismiss_cue(cue)
try:
request = DismissCueRequest.from_dict(self.request.json)
user_models.UserPref.dismiss_cue(request.cue)
return {'message': 'Done'}
except ValueError as e:
self.abort(400, str(e))
# Callers don't use the JSON response for this API call.
return {'message': 'Done'}


def do_get(self, **kwargs):
"""Return a list of the dismissed cue cards"""
Expand Down
Loading

0 comments on commit 3a0b25a

Please sign in to comment.