Стендап Сьогодні

Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті

Підписатись на RSS · 📢 Канал в Telegram @stendap_sogodni

17.10.2024

Перехоплення запитів в інтернеті третьою стороною

Сьогодні натрапив на цікавий випадок: в одного хостингу SMTP не заблокований зовсім, а перехоплюється їхнім внутрішнім сервісом; хочеш свій — доплачуй. Вихід з такої ситуації простий: використовувати натомість Email API.

Але це мене надихнуло написати про те, що, взагалі кажучи, кожна наша взаємодія з інтернетом може бути перехоплена по дорозі. В першу чергу це цікаво робити нашому провайдеру чи хостингу, але також, наприклад, державним установам.


16.10.2024

Подивитися на задачу з іншого боку

В мене тут з історії про перегляд журналів CodeDeploy розвинувся цілий дешборд про стани розгортувань та, оскільки кожний продукт складається з декількох сервісів, кожен з власним розгортуванням — також і про зведені стани продуктів. Про це й буде мова.

В продукту є перелік “останніх розгортувань” кожного сервісу. В розгортування — статус, час початку та кінця, та ще кілька атрибутів як-от гілка. Але головне, що на рівні CodeDeploy немає сутності “розгортування продукту” - тільки по сервісах — та таку сутність я синтезую, оскільки через неї значно природніше дивитися на стан, ніж через мішанину розгортувань.

Визначити статус групи не так легко. Наприклад, якщо всі розгортування мають час кінця, значить, група зараз “в спокої”. Але чи всі сервіси в ній розгорнуті одночасно? Та що таке одночасно? Чи були помилки? Ще цікавіше, коли є активні розгортування. Якщо всі активні — тут все ясно. А коли не всі? Не всі запустилися? Або деякі вже встигли завершитись? Успішно чи не успішно? І так далі.

На початку я дивився на цю задачу саме як агрегацію станів — відповідно, намагався визначити з різних комбінацій, який груповий статус вони представляють: “однорідний”, “неуспішний”, “неоднорідний” (це коли всі успішні, але в різних ревізіях) тощо. Така задача вибухає комбінаторно. Коли на практиці деяка комбінація агрегується не так, як потрібно — то не зрозуміло, куди додавати розгалуження, щоб не порушити іншу логіку.

Тому придумав ось що: все, що мені потрібно — це вибір між декількома станами для групи. Ба більше, ці стани виконують чітку потребу, тому їх легше зрозуміти, ніж абстрактні комбінації статусів. Залишилось для кожного групового стану виписати ознаки: наприклад, “однорідний” значить, що всі розгортування завершені, успішні та відбулися в один час.

В результаті отримую схожий набір розгалужень як в першому варіанті. Але тепер вони мають справжній зміст. Та доповнювати їх буде легше: бо буду знати, з якого стану потрібно “відрізати” умову, та в який додати.

🚶‍♂️ Не випадково, щоб до цього дійти, потрібно було відкласти роботу та сходити на прогулянку.


15.10.2024

PostgreSQL та JSON

PostgreSQL, окрім того, що є вірним вибором бази даних SQL, цілком може також бути вірним вибором бази NoSQL. Принаймні, якщо від NoSQL вам потрібна можливість зберігати JSON, а не здатність до масштабування або всі інші відзнаки, за які зазвичай беруть NoSQL.

Або, практичніше, може в вашій таблиці в PostgreSQL є потреба зберігати прапорці та налаштування, для яких неекономічно створювати окремі стовпчики. Може, це перемикач, який буде увімкнено лише для декількох записів. Може, набір атрибутів без визначеної заздалегідь схеми.

В такому випадку корисно знати, що в PG можна скористатися вбудованими типами json/jsonb. Це дає купу переваг: зміст поля буде гарантовано коректним JSON, за ним можна спеціальними операторами робити пошук. Навіть будувати індекси за окремими атрибутами, включаючи унікальні, та використовувати в умовах JOIN.

Різниця між цими типами в тому, що json це практично text із додатковими перевірками, а jsonb зберігає зміст в оптимізованому для читання двійковому форматі, але трохи більше витрачає на запис.

Єдине, чого PostgreSQL не вміє робити, так це перевіряти зміст поля — окрім його синтаксису. Ну, та ще унікальності атрибута за індексом. Для перевірки за схемою є доповнення pg_jsonschema - його можна додати як перевірку CHECK.


14.10.2024

gin / slog / sentry

Промучився з нібито простою задачею про моніторинг gin - вебсервера на Go.

Для журналювання в slog є slog-gin. Для збору помилок - sentry-go/gin. Для перекидання помилок з журналу в Sentry - slog-sentry - нещодавно вже про це писав. Ніби всі потреби покриті.

Тепер. Помилок є два різновиди: окрім непередбачених панік в Gin можна завжди ввічливо зареєструвати помилку через виклик ctx.Error(). Тут починається… slog-sentry збирає тільки паніки. А slog-gin - тільки “ввічливі” помилки. Ба більше, помилок за один запит може бути декілька, тому slog-gin показує “агреговану” помилку, яка навіть з однією виглядатиме як Error #1: some error, що мене бісить. Також він чомусь не проставляє в журнал атрибут err, а тільки показує помилку як текст.

Виходить, slog-sentry з slog-gin не дружить (хоч і написаний одним автором!) Бо в Sentry потрапить ото агреговане повідомлення про помилки, а сама помилка (чи помилки) - ні — бо вони беруться з err. Якщо додати обидві інтеграції - slog-gin та sentry-gin - то буде не так, як в коді, що не стосується Gin. А окрім того, жодна ще не дивиться на обидві категорії помилок, тож доведеться розвʼязати це два рази окремо.

Можна не інтегруватися з Gin, а надсилати помилки напряму. Тоді все добре (бо вже налаштовано для решти проєкту.) Але ж так ми не отримаємо контексту вебзапиту - IP, URL, та все інше.

Скоріше за все, адаптую інтеграцію slog-gin, щоб вона генерувала структури, потрібні slog-sentry. Та додам обробник, щоб паніки перетворювались в “ввічливі” помилки. Чому все повинно бути так складно?


13.10.2024

Пошук в застосунку SwiftUI

Робив сьогодні пошук для власного застосунку — бо змісту стало достатньо, щоб без пошуку стало складно. Тут два спостереження.

По-перше, пошук — це складно! Навіть просто за рядком. Принаймні, якісний пошук. Та я не знайшов готових рішень. Пошук тільки починається зі збігів за підрядком. Але також обовʼязково треба ігнорувати регістр, причому не тільки в англійській мові — на щастя, зараз я бачу підтримку цього в більшості бібліотек для UTF-8. Далі, пошуковий рядок може міститись не в одному, а в декількох атрибутах обʼєктів, причому збіги в атрибуті “назва” повинен зʼявлятися вище, ніж в атрибуті “нотатки”. Тобто в нас зʼявляється до фільтра ще й ранжування.

Одним словом, розумієш, скільки всього гарного робить ElasticSearch. Знайшов теоретично два рішення: або Search Kit - фреймворк для системного пошуку macOS, який також здатний шукати в межах застосунку. Або повнотекстовий пошук SQLite FTS3 - SQLite взагалі відомий за простоту інтеграції, тож можна користуватися ним суто як рушієм пошуку. Але мені поки вистачає пошуку за підрядком. :)

По-друге, інтерфейс пошуку у SwiftUI виконаний через набір обгорток та користуватися ним досить неприємно, бо всі ці обгортки заплутують логіку. Спочатку є обгортка searchable() - її потрібно додати на… зміст навігаційного контейнера та це дасть там пошуковий рядок. Окрема обгортка searchSuggestions() дозволить описати, як показувати пропозиції результату. А вже всередині пропозицій є обгортка searchCompletion(), яка й робить компонент пропозицією, яку можна обрати. Трохи нагадує час HOC (компонентів вищого порядку) в React.

Принаймні ця архітектура майже не обмежує “бекенд”: ми вільні шукати як завгодно, утворювати пропозиції з будь-чого. Наприклад, за бажанням можна показувати останні переглянуті сторінки, коли рядок пошуку ще порожній або за відсутністю результатів.

Все це щоб зробити звичайний інтерфейс пошуку, як в будь-якому рідному застосунку macOS чи iOS (а точніше, в обох, бо код пошуку є переносним.) Веброзробники до того не звикли — та звісно, можна було взяти звичайне текстове поле та список та зробити з них пошук. Тільки “рідним” воно не буде, та й відтворити всі зручності не так легко.


12.10.2024

Про айтівські неологізми

Потрапила на очі ця стаття про переклади айтівських слів. (Привіт, Стасе!) Вона мене трохи затригерила, принаймні тому, що переклад термінів мені заради цього каналу доводиться робити щодня. Мої думки з досвіду:

Мова — жива. Нові слова запозичуються завжди. Нові уявлення зʼявляються завжди — та потребують нових слів або нового змісту старих слів. Особливо в наш час та особливо в нашій галузі.

Слово “стендап”, бачите, навіть двічі імпортоване з різними змістами. Та йому просто немає перекладу, бо в цього слова свій зміст: це не буквально “нарада стоячи”, а щоденна нарада за методологією Agile. Тому адресатові доведеться вгадувати, що саме криється за “нарадою стоячи”. (Скажи мені “в нас нарада стоячи” та я здивуюся - в нас зазвичай всі сидять.) Або що таке “запит на витяг”. Або “фіксація”. (До речі, це справжній термін — раджу пройтися по статті Вікіпедії про Git - там багато цікавого.)

Або “баг”. Чи це “помилка”? Ні — помилка то “error”, і “хиба” теж. Чи це “вада”? Вада — надто широко, це “flaw”. А баг це суцільно нове поняття. Хочете приклад? “Вадою Sentry є регулярні баги в збиранні помилок.”

Тому я не проти вживання неологізмів там, де впроваджується новий зміст. Навіть коли слово має прямий переклад. Проте очевидно що в багатьох випадках айтівці, що звикли про роботу спілкуватися англійською, просто продовжують вживати звичні слова й в українській мові. Та дійсно, замість “чекни”, “кол”, та “респонсібл” можна було вживати рідні “перевір”, “дзвінок”, “відповідальний”. З цієї статті я знайшов влучний переклад “мітинг” - “нарада” та буду користуватися.

Що з цим робити? Раджу водночас послабити свої очікування від інших та зміцнити пильність до власної мови. Більше спілкуватися, читати та писати українською мовою. Ergo, цей канал. А якщо хочеться шукати цікаві переклади, раджу спільноту Словотвір, згадану в статті вище.


11.10.2024

Javascript for Mac Automation (JXA)

Зробив собі сьогодні шорткат Hyper+J для копіювання поточної задачі з Jira у вигляді AB-123: перезібрати ядро - бо саме такий формат мені потрібний у тексті коміту, та доводиться його писати щоразу вручну.

В мене вже є AppleScript для витягання вкладки. Та в назві вкладки всі дані є, тільки в вигляді [AB-123] перезібрати ядро - JIRA. Ніби перетворити в потрібний вигляд тривіально, втім, в самому AppleScript немає регулярних виразів (та перетворення рядків взагалі виглядають дико). На StackOverflow радять викликати sed. 🥶

Натомість я згадав, що окрім AppleScript, на macOS можна робити автоматизацію звичайнісіньким JavaScript. В якому є звичайнісінькі RegExp. Залишилося перекласти скрипт, додати регулярку та повісити через BetterTouchTool на комбінацію клавіш в Safari. Успіх!

Щодо автоматизації на JavaScript, її можна запускати хоч з термінала командою osascript -l JavaScript copy_jira_issue.js. Єдине, чим вона мені не подобається — синтаксис команд доводиться перекладати з документації з AppleScript. Ось, наприклад, в цьому скрипті є команда app.setTheClipboardTo(). Ніхто в JS так функції не називає — проте це адаптація команди set the clipboard to з AS, де вона читається “як людська мова”. Бо як я розумію, весь цей API здобувається автоматично з тих самих “словників”.

PS. Також сьогодні знайшов чудову довідку про доступ до вкладок різних браузерів з AppleScript, тож можна й для Chrome зробити версію.


10.10.2024

Пост нелюбові до Sentry

Ні, з одного боку я Sentry дуже люблю та додаю до всіх своїх проєктів. Раніше були конкуренти, а тепер не залишилось — принаймні для мене. Особливо мені як поліглоту подобається підтримка всіх уявних мов та фреймворків. А ще вони відкрили код власного сервера, тож його можна розгорнути для себе та не платити.. кхм, тобто замість тарифів Sentry платити системному адміністратору. В мене в самого колись був на сервері Errbit - це був такий альтернативний бекенд для Airbrake, коли він був ще Hoptoad.

Але… Гадаю, Sentry створив для мене більше проблем за всі інші сервіси. Що особливо обурює, так це те що Sentry виконує абсолютно побічну функцію та, на перший погляд, невелику: це ж лише збирачка помилок!

Культура розробки Sentry нагадує молодий проєкт з відкритим кодом, а не недешевий продукт B2B. Зміни вносяться несподівано та документація не завжди збігається.

З Sentry для Ruby в нас була ситуація, що нова версія вступала в нескінченну рекурсію під час збору контексту. Та виводила з ладу весь сервер! Чого збирачка помилок ніколи не повинна робити, то це створювати нові помилки.

Для JavaScript є з десяток пакетів, які покривають різні обставини: Node.js, браузер, React, React Native… якщо у вас в проєкті є декілька “смаків” JavaScript, бажаю успіхів в поєднанні пакетів. Причому звісно ж проблеми доведеться побачити не відразу, а коли відбудеться збіг обставин.

От, останнє що трапилось — вони оновили всі пакети, окрім @sentry/integrations. Та він, як бачу, взагалі зник з репозиторію. Але офіційно цей пакет все ще радять використовувати для React Native. Хоч він не працює: щось помінялося в аргументах функцій. Ой, а документацію планують оновити, вже чотири місяці як. Я поки придумав тільки повністю прибрати ті інтеграції — з втратою важливого функціоналу, але принаймні без помилок.

Знаєте щось дійсно краще? Бо я майже готовий переїхати.


09.10.2024

Віртуалізація для людини-корпорації

Apple (а точніше, Євросоюз) нещодавно порадували тим, що всім продавцям в App Store потрібно буде вказувати контактну інформацію — включаючи навіть поштову адресу. Очевидно, що особисті дані публікувати це божевілля, тому розшукав рішення для таких як я:

Буду радий почути ваші варіанти!


08.10.2024

Вхідні

Мій підхід до керування справами (а може й будь-який?) базується на тому, що є невпорядкований “вхідний” простір, який регулярно потрібно обробляти, переносити у систему та спустошувати. Практично в будь-якій програмі з задачами будуть “вхідні” - Inbox, Things, OmniFocus - це стандартна на наш час функція.

Втім, у своїй програмі я вхідні робити не планував. Я вже маю програму для вхідних нотаток - Drafts - та в ній функцій чи не більше, ніж у типовому менеджері задач: інтеграція з іншими програмами, версія для телефона та годинника тощо. Ба більше, всі вхідні в одну програму не збереш — бо є ще пошта, збережені повідомлення з Slack, та навіть реальні, фізичні предмети на столі.

Тому жодна програма не виконувала мої очікування по обробці вхідних. Довелося доробити свою. Замість нотаток я зробив перелік “вхідних зон”, де ці вхідні можуть накопичуватись. Для кожної зони відстежується час останнього спустошення, та якщо він не сьогодні — час стає червоним, а в сайдбарі з’являється лічильник неперевірених інбоксів.

Таким чином, я не забуду обробити й Drafts, і пошту, і всі інші місця. До речі, оцей Open на ілюстрації — це не статичне посилання, а текст Markdown - воно відкриває застосунки за їхньою URL схемою, наприклад, drafts://. Причому у SwiftUI вбудований рушій Markdown; для статичних рядків він працює сам собою, а змінні достатньо завернути в LocalizedStringKey. Мабуть, для продукту я б таке ризиковане рішення не взяв, але для себе дуже зручно мати Markdown під рукою.