diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1a29f03 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +SHELL := /bin/bash +FILE := tmp.txt +CAT := bat + +# ifeq (, $(shell which bat)) +# $(error "No bat in $(PATH), consider installing it") +# CAT = cat +# endif + + +.PHONY : compile run + +compile : + @ls -1 posts/ | sed -e "s/\..*$$//" | uniq | grep -v 'example'; \ + read -p "Which post? " post; \ + ./node_modules/showdown/bin/showdown.js makehtml -i posts/$$post.md -o posts/$$post.html + +run : + @echo "-> Running" + @npm run startdev \ No newline at end of file diff --git a/app.js b/app.js index 4dfe868..4995447 100644 --- a/app.js +++ b/app.js @@ -38,8 +38,9 @@ app.get('/posts', (req, res) => { } } data.posts.sort(function (a, b) { - var keyA = new Date(a.created_at), - keyB = new Date(b.created_at) + console.log(new Date(a.created_at_short), new Date(b.created_at_short)); + var keyA = new Date(a.created_at_short), + keyB = new Date(b.created_at_short) // Compare the 2 dates if (keyA < keyB) return 1 if (keyA > keyB) return -1 diff --git a/core.css b/core.css index 451ff30..25a6fb7 100644 --- a/core.css +++ b/core.css @@ -4,8 +4,8 @@ html { body { font-family: Hack, Menlo, Monaco, Ubuntu Mono, monospace; font-size: 14px; - color: #ddd; - background: #222; + color: #222; + background: #ddd; padding: 20px; margin: 0; display: flex; @@ -47,10 +47,11 @@ h6 { } blockquote { - border-left: 5px solid #ddd; + border-left: 5px solid #AAA; margin: 0; - padding: 0 20px 0 20px; - margin: 0 0 40px 0; + padding: 10px 20px; + margin: 0 100px 25px 40px; + background-color: #CCC; } .container { @@ -77,24 +78,11 @@ div.site { } a { - color: #fff; + color: #222; } a:hover { - color: #999; -} - -a:visited { - color: #eee; -} - -code { - color: #ddd; - background: #333; -} - -pre { - background: #333; + color: #F28; } .main-menu a, @@ -181,27 +169,24 @@ article li { align-items: baseline; } -code { - color: lightcoral; -} - -pre>code { - color: #ddd; +img { + max-width: 100%; } pre { padding: 20px 10px !important; background: #CCC; + border: 1px solid #888; + border-radius: 4px; } - -pre code { - background: #CCC; -} - pre>code { color: #333; + padding: 0px; } -pre.prettyprint { +code { + color: #333; + background: #CCC; + padding: 2px 6px; border-radius: 4px; } code.prettyprint { @@ -209,12 +194,6 @@ code.prettyprint { padding: 2px 8px; border-radius: 4px; } -code { - color: #333; - background: #CCC; - padding: 2px 6px; - border-radius: 4px; -} .mute { color: #999; diff --git a/package-lock.json b/package-lock.json index ddb98fd..6c936a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -381,6 +381,45 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -513,6 +552,12 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -616,6 +661,12 @@ "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -853,6 +904,15 @@ "unpipe": "~1.0.0" } }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1443,6 +1503,12 @@ } } }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -1831,6 +1897,16 @@ "package-json": "^4.0.0" } }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -2119,6 +2195,30 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", @@ -2148,6 +2248,12 @@ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2351,6 +2457,18 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -2423,6 +2541,12 @@ "send": "0.16.2" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -2466,6 +2590,15 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "showdown": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==", + "dev": true, + "requires": { + "yargs": "^14.2" + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -2982,6 +3115,12 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, "widest-line": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", @@ -2991,6 +3130,45 @@ "string-width": "^2.1.1" } }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "write-file-atomic": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", @@ -3008,11 +3186,82 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } } } } diff --git a/package.json b/package.json index 83dd20c..987677d 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "devDependencies": { "chai": "^4.1.2", "nodemon": "^1.18.10", - "supertest": "^3.0.0" + "supertest": "^3.0.0", + "showdown": "^1.9.1" }, "scripts": { "start": "node app.js", diff --git a/posts/.html b/posts/.html new file mode 100644 index 0000000..53b5f45 --- /dev/null +++ b/posts/.html @@ -0,0 +1,26 @@ +

Better Network Panel - a Chrome extension

+
+

As a Salesforce and Vlocity developer, I'm constantly looking for ways to improve my workflow, speed up my debugging, and find answers fast.

+
+

THE PROBLEM

+

Over the last couple months, part of my debugging process has involved using the Chrome DevTools "Network" panel to find a specific apexremote call. The search to find one apexremote call out of dozens has been… annoying.

+

The page loads, several dozen apexremote calls flood the panel, and I start clicking each one, until the correct one (i.e. Request-Body contains "xyz") and I can proceed to look at the Preview.

+

The issue has only just begun, as I need to inspect the Response, perform some searches for IDs and the like, and although the Response is JSON format, the node in the response I need to search is stringified in a child member. So I must copy the data, parse it somehow, either locally on my machine or on the web (jsoneditoronline.org has been great) and finally perform the searching I need.

+

And all of the above is done several times a day.

+

THE SOLUTION

+

I present to you a "Better Network Panel". A Chrome extension that adds a new panel, and offers great features like:

+ +

Open source baby

+

View it on GitHub

+

Special Thanks

+

A huge thanks and recognition goes to Milton Lai and his project SAML Chrome Panel. I started from a fork of his project, but later started fresh as there was a lot of SAML specific code that this project doesn't use/need. The UI is nearly identical, but the code underneath has become fairly different at this point.

+

The SAML Chrome Panel was a huge help and ispiration! Thank you Milton and contributors to the SAML Chrome Panel project!

+

BNP for Chrome

\ No newline at end of file diff --git a/posts/basic-http-routing-in-golang.html b/posts/basic-http-routing-in-golang.html index 1499be5..a423f9e 100644 --- a/posts/basic-http-routing-in-golang.html +++ b/posts/basic-http-routing-in-golang.html @@ -1,170 +1,117 @@ -

Basic HTTP Routing in Golang

-

Golang is incredibly powerful. Its standard library has so much to offer, but I think other languages have encouraged the - use of external libraries for even the most basic tasks. For example, with JavaScript, most inexperienced developers - seem to use jQuery to do simple tasks like selecting an element and replacing its contents. When you and I both know - jQuery is way overkill for such a task. - See my article on Vanilla JS basics.

-

I believe that in order to be considered an expert in a language, you must at least be able to demonstrate using the core - language to achieve your goal. In our current case, HTTP routing. Now to be clear, I don't think you need to write everything - from scratch all the time, but you should have a firm grasp on what is available by the core language, and when you are - better suited to use an external library. If you are looking for more advanced HTTP routing, then I would suggest using - something like - gin.

+

Basic HTTP Routing in Golang

+

Golang is incredibly powerful. Its standard library has so much to offer, but I think other languages have encouraged the use of external libraries for even the most basic tasks. For example, with JavaScript, most inexperienced developers seem to use jQuery to do simple tasks like selecting an element and replacing its contents. When you and I both know jQuery is way overkill for such a task. See my article on Vanilla JS basics.

+

I believe that in order to be considered an expert in a language, you must at least be able to demonstrate using the core language to achieve your goal. In our current case, HTTP routing. Now to be clear, I don't think you need to write everything from scratch all the time, but you should have a firm grasp on what is available by the core language, and when you are better suited to use an external library. If you are looking for more advanced HTTP routing, then I would suggest using something like gin.

Enough ranting, let's get to it.

Assumptions

-

I assume you have basic knowledge of the Go language at this point, so if not, it might be worth searching for some entry - level basics first. See - A Tour of Go.

-

Let's begin

-

The accompanying repo for the code produced in this article is located - on github.

-

Step 1

+

I assume you have basic knowledge of the Go language at this point, so if not, it might be worth searching for some entry level basics first. See A Tour of Go.

+

Let's begin

+

The accompanying repo for the code produced in this article is located on github.

+

Step 1

Here is our basic folder structure for this basic http routing example:

    basic-http-routing-in-golang/
         main.go
 
-

As a starting point our - main.go file looks like this:

-
    package main
+

As a starting point our main.go file looks like this:

+
    package main
 
     import (
-        "fmt"
-        _ "net/http"
+        "fmt"
+        _ "net/http"
     )
 
     func main() {
-        fmt.Println("Hello HTTP")
+        fmt.Println("Hello HTTP")
     }
 
-

Step 2

-

Now starting at a very basic level, we can leverage the - - http.HandleFunc - method.

+

Step 2

+

Now starting at a very basic level, we can leverage the http.HandleFunc method.

It is very simple to use and its signature is easy to understand.

    func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
 
-

Which basically means, - http.HandleFunc("/url", routingFunction) where - routingFunction looks like this:

+

Which basically means, http.HandleFunc("/url", routingFunction) where routingFunction looks like this:

    func routingFunction(w http.ResponseWriter, req *http.Request) {
-        fmt.Fprint(w, "Hello HTTP")
+        fmt.Fprint(w, "Hello HTTP")
     }
 
-

With - fmt.Fprint() we can pass an - http.ResponseWriter and a message to display. Our browser will now look like this when we visit the - /url endpoint.

-

- Browser Output for Step 2 - Hello HTTP -

-

Here is what - main.go looks like at this point:

+

With fmt.Fprint() we can pass an http.ResponseWriter and a message to display. Our browser will now look like this when we visit the /url endpoint.

+

Browser Output for Step 2 - Hello HTTP

+

Here is what main.go looks like at this point:

    package main
 
     import (
-        "fmt"
-        "log"
-        "net/http"
+        "fmt"
+        "log"
+        "net/http"
     )
 
     func main() {
-        http.HandleFunc("/hello", helloHTTP)
-        log.Fatal(http.ListenAndServe(":8080", nil))
+        http.HandleFunc("/hello", helloHTTP)
+        log.Fatal(http.ListenAndServe(":8080", nil))
     }
 
     func helloHTTP(w http.ResponseWriter, req *http.Request) {
-        fmt.Fprint(w, "Hello HTTP")
+        fmt.Fprint(w, "Hello HTTP")
     }
 
-

Now we could stop there, as this is a "basic" http routing example, but I think it isn't quite useful as an example - yet, until we start to see something slightly more practical.

-

Step 3

-

So let's add a - NotFound page when we don't match a pattern in - HandleFunc. It's as simple as:

+

Now we could stop there, as this is a "basic" http routing example, but I think it isn't quite useful as an example yet, until we start to see something slightly more practical.

+

Step 3

+

So let's add a NotFound page when we don't match a pattern in HandleFunc. It's as simple as:

    func notFound(w http.ResponseWriter, req *http.Request) {
         http.NotFound(w, req)
     }
 
-

Here is what - main.go looks like after that:

+

Here is what main.go looks like after that:

    package main
 
     import (
-        "fmt"
-        "log"
-        "net/http"
+        "fmt"
+        "log"
+        "net/http"
     )
 
     func main() {
-        http.HandleFunc("/hello", helloHTTP)
-        http.HandleFunc("/", notFound)
-        log.Fatal(http.ListenAndServe(":8080", nil))
+        http.HandleFunc("/hello", helloHTTP)
+        http.HandleFunc("/", notFound)
+        log.Fatal(http.ListenAndServe(":8080", nil))
     }
 
     func helloHTTP(w http.ResponseWriter, req *http.Request) {
-        fmt.Fprint(w, "Hello HTTP")
+        fmt.Fprint(w, "Hello HTTP")
     }
 
     func notFound(w http.ResponseWriter, req *http.Request) {
         http.NotFound(w, req)
     }
 
-

This will match - /hello and use the - HelloHTTP method to print "Hello HTTP" to the browser. Any other URLs will get caught by the - / pattern and be given the - http.NotFound response to the browser.

+

This will match /hello and use the HelloHTTP method to print "Hello HTTP" to the browser. Any other URLs will get caught by the / pattern and be given the http.NotFound response to the browser.

So that works, but I think we can go further.

-

Step 4

-

We need to give ourselves something more specific than the simple contrived - /hello endpoint above. So let's assume we are needing to get a user profile. We will use the url - /user/:id where - :id is an identifier used to get the user profile from our persistance layer (i.e. our database).

-

We'll start by creating a new method for this GET request called - userProfile:

+

Step 4

+

We need to give ourselves something more specific than the simple contrived /hello endpoint above. So let's assume we are needing to get a user profile. We will use the url /user/:id where :id is an identifier used to get the user profile from our persistance layer (i.e. our database).

+

We'll start by creating a new method for this GET request called userProfile:

    func userProfile(w http.ResponseWriter, req *http.Request) {
-        userID := req.URL.Path[len("/user/"):]
-        fmt.Fprintf(w, "User Profile: %q", userID)
+        userID := req.URL.Path[len("/user/"):]
+        fmt.Fprintf(w, "User Profile: %q", userID)
     }
 
-

Notice that we get the URL from the - req variable and we treat the string returned from - req.URL.Path as a byte slice to get everything after the - /user/ in the string. - Note: this isn't fool proof, - /user/10ok would get matched here, and we would be assigning - userID to - "10ok". -

-

Let's add this new route in our - main function:

+

Notice that we get the URL from the req variable and we treat the string returned from req.URL.Path as a byte slice to get everything after the /user/ in the string. Note: this isn't fool proof, /user/10ok would get matched here, and we would be assigning userID to "10ok".

+

Let's add this new route in our main function:

    func main() {
-        http.HandleFunc("/hello", helloHTTP)
-        http.HandleFunc("/user/", userProfile)
-        http.HandleFunc("/", notFound)
-        log.Fatal(http.ListenAndServe(":8080", nil))
+        http.HandleFunc("/hello", helloHTTP)
+        http.HandleFunc("/user/", userProfile)
+        http.HandleFunc("/", notFound)
+        log.Fatal(http.ListenAndServe(":8080", nil))
     }
 
-

- Note: that this pattern - /user/ matches the trailing - / so that a call to - /user in the browser would return a - 404 Not Found. -

-

Step 5

-

Ok, so we have introduced some pretty severe holes in the security of our new HTTP router. As mentioned in a note above, - treating the - req.URL.Path as a byte slice and just taking the last half is a terrible idea. So let's fix this:

-
    var validPath = regexp.MustCompile("^/(user)/([0-9]+)$")
+

Note: that this pattern /user/ matches the trailing / so that a call to /user in the browser would return a 404 Not Found.

+

Step 5

+

Ok, so we have introduced some pretty severe holes in the security of our new HTTP router. As mentioned in a note above, treating the req.URL.Path as a byte slice and just taking the last half is a terrible idea. So let's fix this:

+
    var validPath = regexp.MustCompile("^/(user)/([0-9]+)$")
 
     func getID(w http.ResponseWriter, req *http.Request) (string, error) {
         m := validPath.FindStringSubmatch(req.URL.Path)
         if m == nil {
             http.NotFound(w, req)
-            return "", errors.New("Invalid ID")
+            return "", errors.New("Invalid ID")
         }
         return m[2], nil // The ID is the second subexpression.
     }
@@ -175,11 +122,8 @@
         if err != nil {
             return
         }
-        fmt.Fprintf(w, "User Profile: %q", userID)
+        fmt.Fprintf(w, "User Profile: %q", userID)
     }
 

Conclusion

-

For now, I'm calling this "Basic HTTP Routing in Golang" article finished. But I do plan to add more to it as time - allows. Additionally, I'd like to create a more advanced article that discusses the ability to respond to not only GET - requests, but also POST, PUT, and DELETE HTTP methods. Look for an "Advanced HTTP routing in Golang" article - in the future. Thanks for reading this far. I wish you well in your Go endeavors.

+

For now, I'm calling this "Basic HTTP Routing in Golang" article finished. But I do plan to add more to it as time allows. Additionally, I'd like to create a more advanced article that discusses the ability to respond to not only GET requests, but also POST, PUT, and DELETE HTTP methods. Look for an "Advanced HTTP routing in Golang" article in the future. Thanks for reading this far. I wish you well in your Go endeavors.

\ No newline at end of file diff --git a/posts/basic-http-routing-in-golang.md b/posts/basic-http-routing-in-golang.md index 3ef25c3..0135eb2 100644 --- a/posts/basic-http-routing-in-golang.md +++ b/posts/basic-http-routing-in-golang.md @@ -18,13 +18,13 @@ The accompanying repo for the code produced in this article is located [on githu Here is our basic folder structure for this basic http routing example: -
    basic-http-routing-in-golang/
+
    basic-http-routing-in-golang/
         main.go
-
+
As a starting point our `main.go` file looks like this: -
    package main
+
    package main
 
     import (
         "fmt"
@@ -34,7 +34,7 @@ As a starting point our `main.go` file looks like this:
     func main() {
         fmt.Println("Hello HTTP")
     }
-
+
### Step 2 ### @@ -43,14 +43,14 @@ Now starting at a very basic level, we can leverage the [`http.HandleFunc`](http It is very simple to use and its signature is easy to understand.
    func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
-
+
Which basically means, http.HandleFunc("/url", routingFunction) where `routingFunction` looks like this:
    func routingFunction(w http.ResponseWriter, req *http.Request) {
         fmt.Fprint(w, "Hello HTTP")
     }
-
+
With `fmt.Fprint()` we can pass an `http.ResponseWriter` and a message to display. Our browser will now look like this when we visit the `/url` endpoint. @@ -74,7 +74,7 @@ Here is what `main.go` looks like at this point: func helloHTTP(w http.ResponseWriter, req *http.Request) { fmt.Fprint(w, "Hello HTTP") } - + Now we could stop there, as this is a "basic" http routing example, but I think it isn't quite useful as an example yet, until we start to see something slightly more practical. @@ -85,7 +85,7 @@ So let's add a `NotFound` page when we don't match a pattern in `HandleFunc`. I
    func notFound(w http.ResponseWriter, req *http.Request) {
         http.NotFound(w, req)
     }
-
+ Here is what `main.go` looks like after that: @@ -110,7 +110,7 @@ Here is what `main.go` looks like after that: func notFound(w http.ResponseWriter, req *http.Request) { http.NotFound(w, req) } - + This will match `/hello` and use the `HelloHTTP` method to print "Hello HTTP" to the browser. Any other URLs will get caught by the `/` pattern and be given the `http.NotFound` response to the browser. @@ -126,7 +126,7 @@ We'll start by creating a new method for this GET request called `userProfile`: userID := req.URL.Path[len("/user/"):] fmt.Fprintf(w, "User Profile: %q", userID) } - + Notice that we get the URL from the `req` variable and we treat the string returned from `req.URL.Path` as a byte slice to get everything after the `/user/` in the string. **Note: this isn't fool proof, `/user/10ok` would get matched here, and we would be assigning `userID` to `"10ok"`.** @@ -138,7 +138,7 @@ Let's add this new route in our `main` function: http.HandleFunc("/", notFound) log.Fatal(http.ListenAndServe(":8080", nil)) } - + _Note: that this pattern `/user/` matches the trailing `/` so that a call to `/user` in the browser would return a `404 Not Found`._ @@ -156,7 +156,7 @@ Ok, so we have introduced some pretty severe holes in the security of our new HT } return m[2], nil // The ID is the second subexpression. } - + Now we can use this method in our code: @@ -167,7 +167,7 @@ Now we can use this method in our code: } fmt.Fprintf(w, "User Profile: %q", userID) } - + ## Conclusion ## diff --git a/posts/chrome-ext-better-network-panel.html b/posts/chrome-ext-better-network-panel.html new file mode 100644 index 0000000..310f1ae --- /dev/null +++ b/posts/chrome-ext-better-network-panel.html @@ -0,0 +1,26 @@ +

Better Network Panel - a Chrome extension

+
+

"As a Salesforce and Vlocity developer, I'm constantly looking for ways to improve my workflow, speed up my debugging, and find answers fast."

+
+

THE PROBLEM

+

Over the last couple months, part of my debugging process has involved using the Chrome DevTools "Network" panel to find a specific apexremote call. The search to find one apexremote call out of dozens has been… annoying.

+

The page loads, several dozen apexremote calls flood the panel, and I start clicking each one, until the correct one (i.e. Request-Body contains "xyz") and I can proceed to look at the Preview.

+

The issue has only just begun, as I need to inspect the Response, perform some searches for IDs and the like, and although the Response is JSON format, the node in the response I need to search is stringified in a child member. So I must copy the data, parse it somehow, either locally on my machine or on the web (jsoneditoronline.org has been great) and finally perform the searching I need.

+

And all of the above is done several times a day.

+

THE SOLUTION

+

BNP for Chrome

+

I present to you a "Better Network Panel". A Chrome extension that adds a new panel, and offers great features like:

+ +

Open source baby

+

View it on GitHub

+

Special Thanks

+

A huge thanks and recognition goes to Milton Lai and his project SAML Chrome Panel. I started from a fork of his project, but later started fresh as there was a lot of SAML specific code that this project doesn't use/need. The UI is nearly identical, but the code underneath has become fairly different at this point.

+

The SAML Chrome Panel was a huge help and ispiration! Thank you Milton and contributors to the SAML Chrome Panel project!

\ No newline at end of file diff --git a/posts/chrome-ext-better-network-panel.json b/posts/chrome-ext-better-network-panel.json new file mode 100644 index 0000000..b178eba --- /dev/null +++ b/posts/chrome-ext-better-network-panel.json @@ -0,0 +1,9 @@ +{ + "title": "Better Network Panel - a Chrome Extension - Levi Olson", + "permalink": "/posts/chrome-ext-better-network-panel", + "created_at": "2020-10-04T17:55:13-33:00", + "created_at_short": "2020-10-04", + "post_title": "better network panel - a chrome extension", + "active": "posts", + "content_file": "chrome-ext-better-network-panel.html" +} \ No newline at end of file diff --git a/posts/chrome-ext-better-network-panel.md b/posts/chrome-ext-better-network-panel.md new file mode 100644 index 0000000..f3f8494 --- /dev/null +++ b/posts/chrome-ext-better-network-panel.md @@ -0,0 +1,38 @@ +# Better Network Panel - a Chrome extension # + +> "As a Salesforce and Vlocity developer, I'm constantly looking for ways to improve my workflow, speed up my debugging, and find answers fast." + +## THE PROBLEM + +Over the last couple months, part of my debugging process has involved using the Chrome DevTools "Network" panel to find a specific `apexremote` call. The search to find one `apexremote` call out of dozens has been... annoying. + +The page loads, several dozen `apexremote` calls flood the panel, and I start clicking each one, until the correct one (i.e. `Request-Body` contains "xyz") and I can proceed to look at the Preview. + +The issue has only just begun, as I need to inspect the `Response`, perform some searches for `ID`s and the like, and although the `Response` is JSON format, the node in the response I need to search is stringified in a child member. So I must copy the data, parse it somehow, either locally on my machine or on the web (jsoneditoronline.org has been great) and finally perform the searching I need. + +And all of the above is done several times a day. + +## THE SOLUTION + +![BNP for Chrome](/images/bnpscreenshot.png) + +I present to you a "Better Network Panel". A Chrome extension that adds a new panel, and offers great features like: + +* **Full Search** - Entire request is searchable (i.e. headers, postbody, etc...), not just URI +* **JSON Parsing** - Even nested members that contain stringified JSON are parsed +* **JSON Search** - Incremental searching is available directly in the Preview pane +* **Import HAR** - Import your own HAR file and use this tool for debugging +* **Download HAR** - Export a request as a HAR file and use an external tool for further debugging +* **Regex Search** - Powerfull regex searches can be performed on the Preview pane +* More to come + +## Open source baby + +View it on [GitHub](https://github.com/leothelocust/better-network-chrome-panel) + + +## Special Thanks + +A huge thanks and recognition goes to [Milton Lai](https://github.com/milton-lai/saml-chrome-panel) and his project SAML Chrome Panel. I started from a fork of his project, but later started fresh as there was a lot of SAML specific code that this project doesn't use/need. The UI is nearly identical, but the code underneath has become fairly different at this point. + +The SAML Chrome Panel was a huge help and ispiration! Thank you Milton and contributors to the SAML Chrome Panel project! diff --git a/public/images/BNPforChromeScreenshot.png b/public/images/BNPforChromeScreenshot.png new file mode 100644 index 0000000..5aefdec Binary files /dev/null and b/public/images/BNPforChromeScreenshot.png differ diff --git a/public/images/BNPforChromeScreenshotOriginal.png b/public/images/BNPforChromeScreenshotOriginal.png new file mode 100644 index 0000000..5aefdec Binary files /dev/null and b/public/images/BNPforChromeScreenshotOriginal.png differ diff --git a/public/images/bnpscreenshot.png b/public/images/bnpscreenshot.png new file mode 100644 index 0000000..4706573 Binary files /dev/null and b/public/images/bnpscreenshot.png differ diff --git a/views/pages/about.html b/views/pages/about.html index d3f99bc..e029114 100644 --- a/views/pages/about.html +++ b/views/pages/about.html @@ -8,8 +8,8 @@