Skip to content

Commit 6532004

Browse files
committed
Add support for updating full contents
1 parent 7443c66 commit 6532004

File tree

9 files changed

+516
-17
lines changed

9 files changed

+516
-17
lines changed

rfd-api-spec.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,7 @@
12261226
{
12271227
"in": "path",
12281228
"name": "number",
1229+
"description": "The RFD number (examples: 1 or 123)",
12291230
"required": true,
12301231
"schema": {
12311232
"type": "string"
@@ -1250,6 +1251,52 @@
12501251
"$ref": "#/components/responses/Error"
12511252
}
12521253
}
1254+
},
1255+
"post": {
1256+
"operationId": "set_rfd_content",
1257+
"parameters": [
1258+
{
1259+
"in": "path",
1260+
"name": "number",
1261+
"description": "The RFD number (examples: 1 or 123)",
1262+
"required": true,
1263+
"schema": {
1264+
"type": "string"
1265+
}
1266+
}
1267+
],
1268+
"requestBody": {
1269+
"content": {
1270+
"application/json": {
1271+
"schema": {
1272+
"$ref": "#/components/schemas/RfdUpdateBody"
1273+
}
1274+
}
1275+
},
1276+
"required": true
1277+
},
1278+
"responses": {
1279+
"202": {
1280+
"description": "successfully enqueued operation",
1281+
"content": {
1282+
"application/json": {
1283+
"schema": {
1284+
"title": "Null",
1285+
"type": "string",
1286+
"enum": [
1287+
null
1288+
]
1289+
}
1290+
}
1291+
}
1292+
},
1293+
"4XX": {
1294+
"$ref": "#/components/responses/Error"
1295+
},
1296+
"5XX": {
1297+
"$ref": "#/components/responses/Error"
1298+
}
1299+
}
12531300
}
12541301
},
12551302
"/rfd/{number}/attr/{attr}": {
@@ -1352,6 +1399,7 @@
13521399
{
13531400
"in": "path",
13541401
"name": "number",
1402+
"description": "The RFD number (examples: 1 or 123)",
13551403
"required": true,
13561404
"schema": {
13571405
"type": "string"
@@ -4505,7 +4553,13 @@
45054553
"RfdAttrValue": {
45064554
"type": "object",
45074555
"properties": {
4556+
"message": {
4557+
"nullable": true,
4558+
"description": "Optional Git commit message to send with this update (recommended)",
4559+
"type": "string"
4560+
},
45084561
"value": {
4562+
"description": "Full value to set this attribute to in the existing RFD contents",
45094563
"type": "string"
45104564
}
45114565
},
@@ -4524,6 +4578,23 @@
45244578
"published"
45254579
]
45264580
},
4581+
"RfdUpdateBody": {
4582+
"type": "object",
4583+
"properties": {
4584+
"content": {
4585+
"description": "Full Asciidoc content to store for this RFD",
4586+
"type": "string"
4587+
},
4588+
"message": {
4589+
"nullable": true,
4590+
"description": "Optional Git commit message to send with this update (recommended)",
4591+
"type": "string"
4592+
}
4593+
},
4594+
"required": [
4595+
"content"
4596+
]
4597+
},
45274598
"RfdVisibility": {
45284599
"type": "object",
45294600
"properties": {

rfd-api/src/context.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ impl ApiContext {
785785
caller: &ApiCaller,
786786
rfd_number: i32,
787787
content: &str,
788+
message: Option<&str>,
788789
) -> ResourceResult<(), UpdateRfdContentError> {
789790
if caller.any(&[
790791
&ApiPermission::UpdateRfd(rfd_number),
@@ -831,10 +832,16 @@ impl ApiContext {
831832
Err(ResourceError::DoesNotExist)
832833
}
833834
1 => {
835+
let message = format!(
836+
"{}\n\nSubmitted by {}",
837+
message.unwrap_or("RFD API update"),
838+
caller.id
839+
);
840+
834841
// Unwrap is checked by the location length
835842
let location = github_locations.pop().unwrap();
836843
location
837-
.upsert(&rfd_number.into(), content.as_bytes())
844+
.upsert(&rfd_number.into(), content.as_bytes(), &message)
838845
.await
839846
.map_err(UpdateRfdContentError::GitHub)
840847
.to_resource_result()?;

rfd-api/src/endpoints/rfd.rs

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use dropshot::{endpoint, HttpError, HttpResponseOk, Path, Query, RequestContext, TypedBody};
5+
use dropshot::{
6+
endpoint, HttpError, HttpResponseAccepted, HttpResponseOk, Path, Query, RequestContext,
7+
TypedBody,
8+
};
69
use http::StatusCode;
710
use rfd_data::{
811
content::{RfdAsciidoc, RfdContent, RfdDocument, RfdMarkdown},
@@ -28,6 +31,7 @@ use crate::{
2831

2932
#[derive(Debug, Deserialize, JsonSchema)]
3033
pub struct RfdPathParams {
34+
/// The RFD number (examples: 1 or 123)
3135
number: String,
3236
}
3337

@@ -92,6 +96,58 @@ async fn get_rfd_op(
9296
}
9397
}
9498

99+
#[derive(Debug, Deserialize, JsonSchema)]
100+
pub struct RfdUpdateBody {
101+
/// Full Asciidoc content to store for this RFD
102+
content: String,
103+
/// Optional Git commit message to send with this update (recommended)
104+
message: Option<String>,
105+
}
106+
107+
#[trace_request]
108+
#[endpoint {
109+
method = POST,
110+
path = "/rfd/{number}",
111+
}]
112+
pub async fn set_rfd_content(
113+
rqctx: RequestContext<ApiContext>,
114+
path: Path<RfdPathParams>,
115+
body: TypedBody<RfdUpdateBody>,
116+
) -> Result<HttpResponseAccepted<()>, HttpError> {
117+
let ctx = rqctx.context();
118+
let auth = ctx.authn_token(&rqctx).await?;
119+
set_rfd_content_op(
120+
ctx,
121+
&ctx.get_caller(auth.as_ref()).await?,
122+
path.into_inner().number,
123+
body.into_inner(),
124+
)
125+
.await
126+
}
127+
128+
async fn set_rfd_content_op(
129+
ctx: &ApiContext,
130+
caller: &ApiCaller,
131+
number: String,
132+
body: RfdUpdateBody,
133+
) -> Result<HttpResponseAccepted<()>, HttpError> {
134+
if let Ok(rfd_number) = number.parse::<i32>() {
135+
ctx.update_rfd_content(
136+
caller,
137+
rfd_number.into(),
138+
&body.content,
139+
body.message.as_deref(),
140+
)
141+
.await?;
142+
Ok(HttpResponseAccepted(()))
143+
} else {
144+
Err(client_error(
145+
StatusCode::BAD_REQUEST,
146+
"Malformed RFD number",
147+
))
148+
}
149+
}
150+
95151
#[derive(Debug, Deserialize, JsonSchema)]
96152
pub struct RfdAttrPathParams {
97153
number: String,
@@ -162,7 +218,10 @@ async fn get_rfd_attr_op(
162218

163219
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
164220
pub struct RfdAttrValue {
221+
/// Full value to set this attribute to in the existing RFD contents
165222
value: String,
223+
/// Optional Git commit message to send with this update (recommended)
224+
message: Option<String>,
166225
}
167226

168227
/// Set an attribute of a given RFD
@@ -185,7 +244,7 @@ pub async fn set_rfd_attr(
185244
&ctx.get_caller(auth.as_ref()).await?,
186245
path.number,
187246
path.attr,
188-
body.into_inner().value,
247+
&body.into_inner(),
189248
)
190249
.await
191250
}
@@ -196,7 +255,7 @@ async fn set_rfd_attr_op(
196255
caller: &ApiCaller,
197256
number: String,
198257
attr: RfdAttrName,
199-
value: String,
258+
body: &RfdAttrValue,
200259
) -> Result<HttpResponseOk<RfdAttr>, HttpError> {
201260
if let Ok(rfd_number) = number.parse::<i32>() {
202261
// Get the latest revision
@@ -210,10 +269,10 @@ async fn set_rfd_attr_op(
210269

211270
// Update the requested attribute
212271
match &attr {
213-
RfdAttrName::Discussion => content.update_discussion(&value),
214-
RfdAttrName::Labels => content.update_labels(&value),
272+
RfdAttrName::Discussion => content.update_discussion(&body.value),
273+
RfdAttrName::Labels => content.update_labels(&body.value),
215274
RfdAttrName::State => {
216-
let state: RfdState = value.as_str().try_into().map_err(|err| {
275+
let state: RfdState = body.value.as_str().try_into().map_err(|err| {
217276
tracing::info!(?err, "Invalid state was supplied");
218277
HttpError::for_bad_request(None, "Invalid RFD state".to_string())
219278
})?;
@@ -224,7 +283,7 @@ async fn set_rfd_attr_op(
224283
// Persist the data back to GitHub. Note that we do not store this back to the database.
225284
// We rely on GitHub as the source of truth and revisions are required to tbe linked to
226285
// commits
227-
ctx.update_rfd_content(caller, rfd_number, content.raw())
286+
ctx.update_rfd_content(caller, rfd_number, content.raw(), body.message.as_deref())
228287
.await?;
229288

230289
extract_attr(&attr, &content)
@@ -414,6 +473,7 @@ async fn search_rfds_op(
414473

415474
#[derive(Debug, Deserialize, JsonSchema)]
416475
pub struct RfdVisibility {
476+
///
417477
pub visibility: Visibility,
418478
}
419479

rfd-api/src/server.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ use crate::{
2929
device_token::{exchange_device_token, get_device_provider},
3030
},
3131
mappers::{create_mapper, delete_mapper, get_mappers},
32-
rfd::{get_rfd, get_rfd_attr, get_rfds, search_rfds, set_rfd_attr, update_rfd_visibility},
32+
rfd::{
33+
get_rfd, get_rfd_attr, get_rfds, search_rfds, set_rfd_attr, set_rfd_content,
34+
update_rfd_visibility,
35+
},
3336
webhook::github_webhook,
3437
well_known::{jwks_json, openid_configuration},
3538
},
@@ -88,6 +91,8 @@ pub fn server(
8891
// RFDs
8992
api.register(get_rfds).expect("Failed to register endpoint");
9093
api.register(get_rfd).expect("Failed to register endpoint");
94+
api.register(set_rfd_content)
95+
.expect("Failed to register endpoint");
9196
api.register(get_rfd_attr)
9297
.expect("Failed to register endpoint");
9398
api.register(set_rfd_attr)

0 commit comments

Comments
 (0)