"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
improve the json structure
To:
gameoftrees@openbsd.org
Date:
Thu, 28 Mar 2024 10:28:27 +0100

Download raw body.

Thread
This changes the produced json so that the "author" field becomes a
object itself with the following fields

	"author": {
		"full": "Omar Polo <op@openbsd.org>",
		"name": "Omar Polo",
		"mail": "op@openbsd.org",
		"user": "op"
	}

and similarly for the committer.  The only field that is guaranteed to
be always present is author.user since the short formats shows only the
(abbreviated) username.

In the short format we should report "committer" instead of "author"
since it's actually the committer.

To make things consistent, in the "long" format remember the author and
use that for the committer too if not provided.  This way, we can
guaranteed that committer.user is always present in all notifications.

Then, similarly, instead of a single field "message" that is the full
commit message in some cases and the first line in some others, add an
extra field "short_message" that is just the first line.  Again, this is
to guarantee that "short_message" is always present.

There is some redundancy in the JSON now, but I think it's better to
make the consuming easier.

For example, knowing this, to produce notifications in the IRC channel
one only has to extract the id, committer.user and short_message field
(that are now guaranteed to always exist).

ok?

diff /home/op/w/got
commit - 87890bc26c1c6958bd64bb9d46fbc29ba6a92d95
path + /home/op/w/got
blob - 80cfc6f803bbf046beb7285ddb64fd7ef3169391
file + gotd/libexec/got-notify-http/got-notify-http.c
--- gotd/libexec/got-notify-http/got-notify-http.c
+++ gotd/libexec/got-notify-http/got-notify-http.c
@@ -137,6 +137,46 @@ json_field(FILE *fp, const char *key, const char *val,
 	fprintf(fp, "\"%s", comma ? "," : "");
 }
 
+static void
+json_author(FILE *fp, const char *type, char *address, int comma)
+{
+	char	*gt, *lt, *at, *email, *endname;
+
+	fprintf(fp, "\"%s\":{", type);
+
+	gt = strchr(address, '<');
+	if (gt != NULL) {
+		/* long format, e.g. "Omar Polo <op@openbsd.org>" */
+
+		json_field(fp, "full", address, 1);
+
+		endname = gt;
+		while (endname > address && endname[-1] == ' ')
+			endname--;
+
+		*endname = '\0';
+		json_field(fp, "name", address, 1);
+
+		email = gt + 1;
+		lt = strchr(email, '>');
+		if (lt)
+			*lt = '\0';
+
+		json_field(fp, "mail", email, 1);
+
+		at = strchr(email, '@');
+		if (at)
+			*at = '\0';
+
+		json_field(fp, "user", email, 0);
+	} else {
+		/* short format only shows the username */
+		json_field(fp, "user", address, 0);
+	}
+
+	fprintf(fp, "}%s", comma ? "," : "");
+}
+
 static int
 jsonify_short(FILE *fp)
 {
@@ -175,9 +215,9 @@ jsonify_short(FILE *fp)
 
 		fprintf(fp, "{\"short\":true,");
 		json_field(fp, "id", id, 1);
-		json_field(fp, "author", author, 1);
+		json_author(fp, "committer", author, 1);
 		json_field(fp, "date", date, 1);
-		json_field(fp, "message", message, 0);
+		json_field(fp, "short_message", message, 0);
 		fprintf(fp, "}");
 	}
 
@@ -194,6 +234,7 @@ static int
 jsonify(FILE *fp)
 {
 	const char	*errstr;
+	char		*author = NULL;
 	char		*l;
 	char		*line = NULL;
 	size_t		 linesize = 0;
@@ -242,7 +283,13 @@ jsonify(FILE *fp)
 			if (strncmp(l, "from: ", 6) != 0)
 				errx(1, "unexpected from line");
 			l += 6;
-			json_field(fp, "author", l, 1);
+
+			author = strdup(l);
+			if (author == NULL)
+				err(1, "strdup");
+
+			json_author(fp, "author", l, 1);
+
 			phase = P_VIA;
 			break;
 
@@ -250,10 +297,17 @@ jsonify(FILE *fp)
 			/* optional */
 			if (!strncmp(l, "via: ", 5)) {
 				l += 5;
-				json_field(fp, "via", l, 1);
+				json_author(fp, "committer", l, 1);
 				phase = P_DATE;
 				break;
 			}
+
+			if (author == NULL) /* impossible */
+				err(1, "from not specified");
+			json_author(fp, "committer", author, 1);
+			free(author);
+			author = NULL;
+
 			phase = P_DATE;
 			/* fallthrough */
 
@@ -301,7 +355,6 @@ jsonify(FILE *fp)
 			if (errstr)
 				errx(1, "message len is %s: %s", errstr, l);
 
-			fprintf(fp, "\"message\":\"");
 			phase = P_MSG;
 			break;
 
@@ -317,14 +370,21 @@ jsonify(FILE *fp)
 			 * tolerate one byte less than advertised.
 			 */
 			if (*l == ' ') {
-				escape(fp, l + 1); /* skip leading space */
+				l++; /* skip leading space */
+				linelen--;
 
-				/* avoid pre-pending \n to the commit msg */
-				msgwrote += linelen - 1;
-				if (msgwrote != 0)
+				if (msgwrote == 0 && linelen != 0) {
+					json_field(fp, "short_message", l, 1);
+					fprintf(fp, "\"message\":\"");
+					escape(fp, l);
 					escape(fp, "\n");
+					msgwrote += linelen;
+				} else if (msgwrote != 0) {
+					escape(fp, l);
+					escape(fp, "\n");
+				}
 			}
-			msglen -= linelen;
+			msglen -= linelen + 1;
 			if (msglen <= 1) {
 				fprintf(fp, "\",");
 				msgwrote = 0;
blob - 6892df7536c1a19134db4827c42f870d903c65a7
file + regress/gotd/http_notification.sh
--- regress/gotd/http_notification.sh
+++ regress/gotd/http_notification.sh
@@ -62,8 +62,20 @@ test_file_changed() {
 	{"notifications":[{
 		"short":false,
 		"id":"$commit_id",
-		"author":"$GOT_AUTHOR",
+		"author":{
+			"full":"$GOT_AUTHOR",
+			"name":"$GIT_AUTHOR_NAME",
+			"mail":"$GIT_AUTHOR_EMAIL",
+			"user":"$GOT_AUTHOR_11"
+		},
+		"committer":{
+			"full":"$GOT_AUTHOR",
+			"name":"$GIT_AUTHOR_NAME",
+			"mail":"$GIT_AUTHOR_EMAIL",
+			"user":"$GOT_AUTHOR_11"
+		},
 		"date":"$d",
+		"short_message":"make changes",
 		"message":"make changes\n",
 		"diffstat":{},
 		"changes":{}
@@ -139,8 +151,20 @@ test_many_commits_not_summarized() {
 		{
 			"short":false,
 			"id":"$commit_id",
-			"author":"$GOT_AUTHOR",
+			"author":{
+				"full":"$GOT_AUTHOR",
+				"name":"$GIT_AUTHOR_NAME",
+				"mail":"$GIT_AUTHOR_EMAIL",
+				"user":"$GOT_AUTHOR_11"
+			},
+			"committer":{
+				"full":"$GOT_AUTHOR",
+				"name":"$GIT_AUTHOR_NAME",
+				"mail":"$GIT_AUTHOR_EMAIL",
+				"user":"$GOT_AUTHOR_11"
+			},
 			"date":"$commit_time",
+			"short_message":"make changes",
 			"message":"make changes\n",
 			"diffstat":{},
 			"changes":{}
@@ -220,9 +244,11 @@ test_many_commits_summarized() {
 		{
 			"short":true,
 			"id":"$commit_id",
-			"author":"$GOT_AUTHOR_8",
+			"committer":{
+				"user":"$GOT_AUTHOR_8"
+			},
 			"date":"$commit_time",
-			"message":"make changes"
+			"short_message":"make changes"
 		}
 		EOF
 	done >> $testroot/stdout.expected