Skip to content

Commit 919d872

Browse files
Merge pull request #31 from pebble-dev/dev-portal-updates
Dev portal updates
2 parents 141af33 + 9149549 commit 919d872

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed

appstore/developer_portal_api.py

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ def submit_new_app():
103103
appinfo_valid, appinfo_validation_error = is_valid_appinfo(appinfo)
104104
if not appinfo_valid:
105105
return jsonify(error=f"The appinfo.json in your pbw file has the following error: {appinfo_validation_error}", e="invalid.appinfocontent"), 400
106+
107+
if params["type"] == "watchface" and not appinfo["watchapp"]["watchface"]:
108+
return jsonify(error=f"You selected the app type 'Watchface'. This does not match the configuration in your appinfo.json", e="invalid.appinfocontent"), 400
109+
elif params["type"] == "watchapp" and appinfo["watchapp"]["watchface"]:
110+
return jsonify(error=f"You selected the app type 'Watch App'. This does not match the configuration in your appinfo.json", e="invalid.appinfocontent"), 400
106111

107112
# Check app doesn't already exist
108113
try:
@@ -139,7 +144,6 @@ def submit_new_app():
139144

140145
# Remove any platforms with no screenshots
141146
screenshots = {k: v for k, v in screenshots.items() if v}
142-
143147
app_obj = App(
144148
id=id_generator.generate(),
145149
app_uuid=appinfo['uuid'],
@@ -157,7 +161,7 @@ def submit_new_app():
157161
hearts=0,
158162
releases=[],
159163
icon_large=upload_asset(request.files['large_icon'], request.files["large_icon"].content_type),
160-
icon_small=upload_asset(request.files['small_icon'], request.files["small_icon"].content_type) if 'small_icon' in params else '',
164+
icon_small=upload_asset(request.files['small_icon'], request.files["small_icon"].content_type) if 'small_icon' in request.files else '',
161165
source=params['source'] if 'source' in params else "",
162166
title=params['title'],
163167
type=params['type'],
@@ -180,7 +184,7 @@ def submit_new_app():
180184
algolia_index.partial_update_objects([algolia_app(app_obj)], { 'createIfNotExists': True })
181185

182186
try:
183-
announce_new_app(app_obj)
187+
announce_new_app(app_obj, pbw.is_generated())
184188
except Exception:
185189
# We don't want to fail just because Discord is being weird
186190
print("Discord is being weird")
@@ -309,7 +313,7 @@ def submit_new_release(app_id):
309313
db.session.commit()
310314

311315
try:
312-
announce_release(app, release_new)
316+
announce_release(app, release_new, pbw.is_generated())
313317
except Exception:
314318
# We don't want to fail just because Discord webhook is being weird
315319
print("Discord is being weird")
@@ -522,6 +526,68 @@ def delete_banner(app_id, platform, banner_id):
522526
db.session.commit()
523527
return jsonify(success=True, message=f"Deleted banner {banner_id}", id=banner_id, platform=platform)
524528

529+
@devportal_api.route('/app/<app_id>/icons', methods=['GET'])
530+
def get_app_icons(app_id):
531+
try:
532+
app = App.query.filter(App.id == app_id).one()
533+
except NoResultFound as e:
534+
return jsonify(error="Unknown app", e="app.notfound"), 404
535+
536+
537+
return jsonify(small=app.icon_small, large=app.icon_large)
538+
539+
@devportal_api.route('/app/<app_id>/icon/<size>', methods=['GET'])
540+
def get_app_icon(app_id, size):
541+
if size not in ("large", "small"):
542+
return jsonify(error="Invalid icon size. Expected 'small' or 'large'.", e="size.invalid"), 404
543+
544+
try:
545+
app = App.query.filter(App.id == app_id).one()
546+
except NoResultFound as e:
547+
return jsonify(error="Unknown app", e="app.notfound"), 404
548+
549+
out = app.icon_small if size == "small" else app.icon_large
550+
return jsonify(out)
551+
552+
@devportal_api.route('/app/<app_id>/icon/<size>', methods=['POST'])
553+
def new_app_icon(app_id, size):
554+
if size not in ("large", "small"):
555+
return jsonify(error="Invalid icon size. Expected 'small' or 'large'.", e="size.invalid"), 404
556+
557+
try:
558+
app = App.query.filter(App.id == app_id).one()
559+
except NoResultFound as e:
560+
return jsonify(error="Unknown app", e="app.notfound"), 404
561+
562+
# Check we own the app
563+
if not is_users_developer_id(app.developer_id):
564+
return jsonify(error="You do not have permission to modify that app", e="permission.denied"), 403
565+
566+
# Get the image, this is a single image API
567+
if "icon" in request.files:
568+
new_image = request.files["icon"]
569+
else:
570+
return jsonify(error="Missing file: icon", e="icon.missing"), 400
571+
572+
# Check it's a valid image file
573+
if not is_valid_image_file(new_image):
574+
return jsonify(error="Illegal image type", e="icon.illegalvalue"), 400
575+
576+
# Check it's the correct size
577+
if not is_valid_image_size(new_image, f"{size}_icon"):
578+
max_w, max_h = get_max_image_dimensions("icon")
579+
return jsonify(error="Invalid image size", e="icon.illegaldimensions", message=f"Image should be {max_w}x{max_h}"), 400
580+
581+
new_image_id = upload_asset(new_image, new_image.content_type)
582+
if size == "large":
583+
app.icon_large = new_image_id
584+
elif size == "small":
585+
app.icon_small = new_image_id
586+
db.session.commit()
587+
588+
return jsonify(success=True, id=new_image_id, size=size)
589+
590+
525591

526592
@devportal_api.route('/wizard/rename/<developer_id>', methods=['POST'])
527593
def wizard_rename_developer(developer_id):

appstore/discord.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
def random_party_emoji():
1212
return random.choice(party_time_emoji)
1313

14-
def announce_release(app, release):
14+
def announce_release(app, release, is_generated):
1515
release_notes = release.release_notes
1616
if not release_notes:
1717
release_notes = "N/A"
@@ -35,9 +35,9 @@ def announce_release(app, release):
3535
}]
3636
}
3737

38-
send_discord_webhook(request_data)
38+
send_discord_webhook(request_data, is_generated)
3939

40-
def announce_new_app(app):
40+
def announce_new_app(app, is_generated):
4141
request_fields = [{
4242
"name": "Name",
4343
"value": app.title
@@ -70,11 +70,13 @@ def announce_new_app(app):
7070
"value": app.website
7171
})
7272

73+
txt_type = app.type if not is_generated else "Generated Watchface"
74+
7375
request_data = {
7476
"embeds": [{
75-
"title": f"New {str(app.type).capitalize()} Alert {random_party_emoji()}",
77+
"title": f"New {str(txt_type).capitalize()} Alert {random_party_emoji()}",
7678
"url": f"{config['APPSTORE_ROOT']}/application/{app.id}",
77-
"description": f"There's a new {app.type} on the appstore!",
79+
"description": f"There's a new {txt_type} on the appstore!",
7880
"thumbnail": {
7981
"url": generate_image_url(app.icon_large, 80, 80, True, True),
8082
"height": 80,
@@ -83,8 +85,8 @@ def announce_new_app(app):
8385
"fields": request_fields
8486
}]
8587
}
86-
87-
send_discord_webhook(request_data)
88+
89+
send_discord_webhook(request_data, is_generated)
8890

8991
def audit_log(operation):
9092
request_fields = [{
@@ -112,10 +114,15 @@ def audit_log(operation):
112114

113115
send_admin_discord_webhook(request_data)
114116

115-
def send_discord_webhook(request_data):
116-
if config['DISCORD_HOOK_URL'] is not None:
117-
headers = {'Content-Type': 'application/json'}
118-
requests.post(config['DISCORD_HOOK_URL'], data=json.dumps(request_data), headers=headers)
117+
def send_discord_webhook(request_data, is_generated = False):
118+
if not is_generated:
119+
if config['DISCORD_HOOK_URL'] is not None:
120+
headers = {'Content-Type': 'application/json'}
121+
requests.post(config['DISCORD_HOOK_URL'], data=json.dumps(request_data), headers=headers)
122+
else:
123+
if config['DISCORD_GENERATED_HOOK_URL'] is not None:
124+
headers = {'Content-Type': 'application/json'}
125+
requests.post(config['DISCORD_GENERATED_HOOK_URL'], data=json.dumps(request_data), headers=headers)
119126

120127
def send_admin_discord_webhook(request_data):
121128
if config['DISCORD_ADMIN_HOOK_URL'] is not None:

appstore/pbw.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .utils import id_generator
1414

1515
PLATFORMS = ['aplite', 'basalt', 'chalk', 'diorite', 'emery']
16+
GENERATED_ID_PREFIX = "13371337"
1617

1718
class PBW(object):
1819
MANIFEST_FILENAME = 'manifest.json'
@@ -121,6 +122,9 @@ def get_app_metadata(self):
121122
}
122123
return self.header
123124

125+
def is_generated(self):
126+
return str(self.get_app_metadata()["uuid"]).startswith(GENERATED_ID_PREFIX)
127+
124128
def close(self):
125129
self.zip.close()
126130

0 commit comments

Comments
 (0)