Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenAPI: feature_api #4263

Draft
wants to merge 78 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
63aafc9
verbose feature dict
markxiong0122 Aug 20, 2024
4ba34bf
stagedict
markxiong0122 Aug 20, 2024
2b149fa
nested object
markxiong0122 Aug 20, 2024
5db1ef4
extension obj
markxiong0122 Aug 20, 2024
9fcb27f
remove flask & change basehandler
markxiong0122 Aug 20, 2024
6edf33d
Add shipping_year field. (#4257)
jrobbins Aug 20, 2024
ce04546
Disable OT milestone edits during automated OT creation process (serv…
DanielRyanSmith Aug 21, 2024
38a06c8
Implement fulltext search within a specific field. (#4262)
jrobbins Aug 21, 2024
51b4926
Disable changing OT milestones when OT creation process is in progres…
DanielRyanSmith Aug 22, 2024
10ff067
Handle OpenAPI models correctly for GET requests (#4274)
DanielRyanSmith Aug 22, 2024
3df0840
Delete code for the guide edit handler. (#4270)
jrobbins Aug 22, 2024
34cf3cd
handle ID key in changes dictionary (#4265)
DanielRyanSmith Aug 22, 2024
f4875c6
Allow searching for shipping year (#4268)
jrobbins Aug 22, 2024
fbe07e7
OpenAPI: comments_api (#4195)
markxiong0122 Aug 23, 2024
532109e
OpenAPI: component_users (#4199)
markxiong0122 Aug 23, 2024
dd4a0a4
token refresh (#4255)
markxiong0122 Aug 23, 2024
a3a26cc
OpenAPI: review_api (#4250)
markxiong0122 Aug 23, 2024
9d2de18
fix ot prereqs typo (#4276)
DanielRyanSmith Aug 23, 2024
7ba3658
Obtain Chromium source files concurrently (#4266)
DanielRyanSmith Aug 23, 2024
e8f4661
Add default string conversion for dates in JSON responses (#4280)
DanielRyanSmith Aug 24, 2024
d02bcaa
Make certain props in feature links nullable (#4281)
DanielRyanSmith Aug 25, 2024
ba2ecd3
crossing todos (#4283)
markxiong0122 Aug 26, 2024
d4e1056
Change postToThreadType to type int. (#4291)
jrobbins Aug 26, 2024
419a1de
Fix search for phrases that are one word repeated. (#4290)
jrobbins Aug 27, 2024
e1c2d6c
Set shipping_year initial value only on feature creation. (#4301)
jrobbins Aug 27, 2024
1d3022d
Make detection of intent_thread_url more robust (#4300)
jrobbins Aug 27, 2024
a1aa28e
roadmap bug fix (#4304)
DanielRyanSmith Aug 28, 2024
3ed6385
Run mypy on generated openapi code too (#4303)
jrobbins Aug 29, 2024
7029cd7
npm: bump rollup from 4.21.0 to 4.21.1 (#4293)
dependabot[bot] Aug 29, 2024
32dc76e
npm: bump @babel/preset-env from 7.25.3 to 7.25.4 (#4294)
dependabot[bot] Aug 29, 2024
d841c89
OpenAPI: login logout (#4220)
markxiong0122 Aug 29, 2024
2e0800e
OpenAPI: settings_api (#4253)
markxiong0122 Aug 29, 2024
6389daa
OpenAPI: processes_api (#4239)
markxiong0122 Aug 29, 2024
6e22d7d
OpenAPI: OT (#4226)
markxiong0122 Aug 29, 2024
dd51bc4
npm: bump eslint from 9.9.0 to 9.9.1 (#4297)
dependabot[bot] Aug 29, 2024
ffbb3c0
npm: bump @types/node from 22.4.1 to 22.5.1 (#4310)
dependabot[bot] Aug 29, 2024
ea25c1f
Hide and ignore use counter field for deprecation trials (#4278)
DanielRyanSmith Aug 29, 2024
00e4729
Finish new pagination implementation. (#4269)
KyleJu Aug 29, 2024
6beae62
Clarify that the security gate is for WP security. (#4309)
jrobbins Aug 29, 2024
dab0d2d
Update extension approved email to use 'extension' (#4288)
DanielRyanSmith Aug 29, 2024
4b89f4e
Add simple warning header text to OT creation form (#4277)
DanielRyanSmith Aug 29, 2024
897f7a8
Add `clean-setup` step to deployment (#4314)
DanielRyanSmith Aug 29, 2024
2ea0e2f
OpenAPI: stars_api (#4254)
markxiong0122 Aug 30, 2024
153fcde
npm: bump axios and @openapitools/openapi-generator-cli (#4311)
dependabot[bot] Aug 30, 2024
ee836d2
Record an Activity when assigning a reviewer. (#4316)
jrobbins Aug 30, 2024
88e46c8
Fix "WP Security" in gate sorting. (#4318)
jrobbins Aug 30, 2024
e74deff
sl-select for page sizet (#4315)
KyleJu Sep 3, 2024
e8bdfd3
npm: bump rollup from 4.21.1 to 4.21.2 (#4323)
dependabot[bot] Sep 3, 2024
9d99cf4
Limit pagination length (#4328)
KyleJu Sep 3, 2024
97e79c1
Pressing slash focuses on feature searchbox. (#4325)
jrobbins Sep 3, 2024
58b879e
Add margin to separate searchbox from results. (#4329)
jrobbins Sep 3, 2024
53bb58f
npm: bump @types/node from 22.5.1 to 22.5.2 (#4324)
dependabot[bot] Sep 4, 2024
f6fa250
Bump flask-cors from 4.0.1 to 5.0.0 (#4326)
dependabot[bot] Sep 4, 2024
0a338c0
Avoid parsing the period at end of intent thread links. (#4331)
jrobbins Sep 4, 2024
c77cd1e
Add gate rationale (#4319)
jrobbins Sep 4, 2024
5ff8a21
OT Creation allows for the selection of WebDXFeature use counters (cl…
DanielRyanSmith Sep 5, 2024
0697666
OT Creation allows for the selection of WebDXFeature use counters (se…
DanielRyanSmith Sep 5, 2024
f4731a9
Revised testing gate rationale. (#4336)
jrobbins Sep 5, 2024
b88d7f9
Avoid a race condition when processing LGTMs. (#4334)
jrobbins Sep 5, 2024
c552277
OT creation form deprecation trial bug fix (#4338)
DanielRyanSmith Sep 6, 2024
e546e41
npm: bump typescript from 5.5.4 to 5.6.2 (#4342)
dependabot[bot] Sep 9, 2024
64a88dc
npm: bump @playwright/test from 1.46.1 to 1.47.0 (#4343)
dependabot[bot] Sep 9, 2024
5c59429
npm: bump @web/test-runner from 0.18.3 to 0.19.0 (#4344)
dependabot[bot] Sep 10, 2024
eef5158
npm: bump eslint from 9.9.1 to 9.10.0 (#4345)
dependabot[bot] Sep 10, 2024
c8ad386
npm: bump @types/node from 22.5.2 to 22.5.4 (#4346)
dependabot[bot] Sep 10, 2024
eaa2c96
npm: bump ws from 7.5.9 to 7.5.10 (#4348)
dependabot[bot] Sep 10, 2024
fc488c9
Separate creation and setup requests in OT creation process (#4214)
DanielRyanSmith Sep 12, 2024
419c936
Add OT support emails secret for OT creation (#4287)
DanielRyanSmith Sep 12, 2024
dc68cdf
Make autocomplete offer enum values separately. (#4349)
jrobbins Sep 12, 2024
296c2b4
Add script to backfill shipping_year (#4357)
jrobbins Sep 13, 2024
d821118
Add defensive coding to BackfillShippingYear (#4361)
jrobbins Sep 13, 2024
0b9ff3d
rebase
markxiong0122 Aug 20, 2024
5a2e31c
stagedict
markxiong0122 Aug 20, 2024
c9be86a
nested object
markxiong0122 Aug 20, 2024
9673840
extension obj
markxiong0122 Aug 20, 2024
a8109df
remove flask & change basehandler
markxiong0122 Aug 20, 2024
11eeb01
rebase
markxiong0122 Sep 16, 2024
080ebde
resolve rebase conflicts
markxiong0122 Sep 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 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
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
1 change: 1 addition & 0 deletions api/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,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
3 changes: 2 additions & 1 deletion 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
2 changes: 1 addition & 1 deletion api/feature_links_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ def do_get(self, **kwargs):
type = self.request.args.get('type', None)
is_error = self.get_bool_arg('is_error', None)
if domain:
return FeatureLinksSample.from_dict(get_feature_links_samples(domain, type, is_error))
return FeatureLinksSample.from_dict(get_feature_links_samples(domain, type, is_error))
5 changes: 4 additions & 1 deletion api/features_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from typing import Any
from google.cloud import ndb

from chromestatus_openapi.models import (VerboseFeatureDict as OneFeatureResponse, FeatureSearchResponse)

from api import api_specs
from api import converters
from framework import basehandlers
Expand Down Expand Up @@ -113,7 +115,8 @@ def do_get(self, **kwargs):
# way to handle this in a strictly-typed manner and implement it.
feature_id = kwargs.get('feature_id', None)
if feature_id:
return self.get_one_feature(feature_id)
feature = OneFeatureResponse.from_dict(self.get_one_feature(feature_id))
return feature.to_dict()
return self.do_search()

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

import logging

import google.oauth2.id_token
import werkzeug.exceptions
from chromestatus_openapi.models import SignInRequest
from google.auth.transport import requests

from framework import basehandlers
from framework import users
import settings
from framework import basehandlers, users


class LoginAPI(basehandlers.APIHandler):
Expand All @@ -31,8 +30,14 @@ def do_get(self, **kwargs):
self.abort(405, valid_methods=['POST'])

def do_post(self, **kwargs):
token = self.get_param('credential')
message = "Unable to Authenticate. Please sign in again."
try:
request = SignInRequest.from_dict(self.request.json)
token = request.credential
if not token:
raise werkzeug.exceptions.BadRequest(description="Missing required field 'credential'")
message = "Unable to Authenticate. Please sign in again."
except ValueError:
message = "Invalid Request"

try:
idinfo = google.oauth2.id_token.verify_oauth2_token(
Expand Down
Loading
Loading