<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های مصطفی مرادیان</title>
        <link>https://virgool.io/feed/@mostafamoradian0</link>
        <description>Developer</description>
        <language>fa</language>
        <pubDate>2026-06-07 13:13:56</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/9347/avatar/DW7cpm.png?height=120&amp;width=120</url>
            <title>مصطفی مرادیان</title>
            <link>https://virgool.io/@mostafamoradian0</link>
        </image>

                    <item>
                <title>طراحی، خودکارسازی و جمع‌آوری نتایج تست پروژه gREST</title>
                <link>https://virgool.io/coderlife/grest-test-automation-and-continuous-integration-iqjyvxjgntsi</link>
                <description>توی نوشته قبلی که راجع به gREST و علل بوجود اومدنش نوشتم، به این موضوع اشاره کردم که براش با استفاده از pytest، یونیت تست نوشتم، اجرای تست‌ها رو هم با استفاده از Travis-CI خودکار کردم و از سرویس Coveralls برای استخراج نتایج استفاده کردم. اینکه بتونم نرم‌افزار آزاد/متن‌باز خوبی رو بنویسم که برای انجام کار قابل اعتماد باشه و تست‌هاش تقریبا تمام کد رو دربر بگیره، خیلی برام مهم بود و به همین دلیل از سرویس‌های موجود استفاده کردم. همچنین از FOSSA برای بررسی تطابق مجوزهای نرم‌افزاری استفاده کردم. به همین خاطر توی این نوشته می‌خوام روند این کار و اینکه چطور تست‌ها رو نوشتم و از چه روش‌هایی استفاده کردم، براتون بگم.اولین نکته اینه که چرا از pytest استفاده کردم. مهم‌ترین و بهترین علتش اینه که کار باهاش ساده‌اس و یه سری افزونه داره که میشه باهاش (مثلا) Flask رو تست کرد. و چون gREST برمبنای Flask نوشته شده، طبیعتا gREST رو هم میشه تست کرد. اسم اون افزونه pytest-flask هستش و می‌تونید با کمترین دردسر راه‌اندازی و اجراش کنید. دومین نکته هم اینه که هر وقت فرصت کردم یه متن آموزشی در مورد نحوه ساخت یک Restful API با استفاده از gREST می‌نویسم و هدفم از این نوشته فقط موضوع تست هستش.طبیعتا برای تست Restful API باید HTTP verbs که پشتیبانی می‌کنه رو تست کرد و پارامترهای ورودی مختلف رو روش چک کرد و خروجی رو بررسی کرد. توی gREST افعال GET, POST, PUT, PATCH و DELETE رو پیاده‌سازی کردم، در نتیجه برای این پنج فعل و تمام ورودی‌هاش تست نوشتم.سه تا فایل تست برای gREST وجود داره که یکی برای تست کردن مولفه validation_rules هستش و دو تای دیگه برمبنای دو تا app هستن که تو مسیر examples وجود دارند. اولین اپ، یه endpoint ساده است که به یه مدل توی دیتابیس به اسم Person اشاره داره و اون یکی یه اپ پیچیده‌تره که رابطه‌ی Person و Pet داخلش تست میشه و رابطه این بین توسط PetInfo مدیریت میشه. در واقع یه nested endpoint به وجود میاد.برای مثال، در app ساده در کل یه endpoint وجود داره به اسم persons/ که با POST کردن یک JSON با استفاده از یک Rest Client (مثل Postman) می‌تونید یه شخص جدید ایجاد کنید، به همین سادگی. البته دیگه نصب و راه‌اندازی پایتون و Neo4j از نظر من در این مطلب مفروضه.در app دوم، یک persons/ وجود داره که می‌تونه یک یا چند حیوان خانگی داشته باشه: pets/. برای اینکه بتونیم این app رو تست کنیم، باید یک شخص، یک حیوان خانگی و یک رابطه بین این‌ها ایجاد کنیم که توی تست‌ها می‌تونید ببینید.یک نمونه تست از کد تستی که نوشتم این شکلیه:تو این قسمت می‌خوایم بدونیم که شخصی در پایگاه داده وجود داره یا خیر. طبیعتا توی اولین تست، چون ما داده اولیه توی پایگاه داده ایجاد نکردیم، چیزی وجود نداره و انتظار میره که API هم همچین پیغامی رو به ما بده یعنی خطای 404 و پیغام No person exists. باقی تست‌ها هم به همین منوال هستند یعنی یک فعل و یک سری پارامتر ورودی و یک جواب یا خطای مورد انتظار از API. برای مثال همین 4 خط بالا (به علاوه یک خط توضیح)، متد index داخل grest.py رو تست می‌کنه.تا اینجا یه سری مفاهیم و فایل‌های مهم برای تست رو بهش اشاره کردم. حالا می‌خوام بگم که چطور این تست اجرا میشه و خروجی میده. ساده‌ترین راه برای اجرای این تست، اجرای دستور pytest داخل مسیر پروژه است. با این کار pytest، تست‌های شما رو جمع می‌کنه (اصطلاحاً collect می‌کنه) و یک به یک اجراشون می‌کنه. البته اگر بخوایم اطلاعات پوشش کد که پایین‌تر توضیح میدم رو هم داشته باشیم باید از دستور coverage استفاده کنیم.این کار ساده رو می‌شه خودکارش کرد که هر وقت کد رو تغییر می‌دید و روی git، ارسال (push) می‌کنید، سیستمی/سرویسی وجود داشته باشه که خودش تست رو انجام بده و نیازی نباشه که این کار رو دستی انجام بدید. یکی از اون سیستم‌ها/سرویس‌ها اسمش Travis-CI هست و از اسمش پیداست که برای Continuous Integration (یکپارچه‌سازی مداوم) بکار میره.روند کارش هم ساده است. کافیه یه حساب کاربری تو وب‌سایتش ایجاد کنید و پروژه‌تون رو داخلش ثبت کنید. بعدش یه فایل ایجاد کنید توی repo خودتون به اسم travis.yml. و تنظیمات رو داخلش به همراه نسخه پایتون و Neo4j و دیگر تنظیمات مورد نظرتون وارد می‌کنید و منتظر اجرای تست از سمت Travis-CI بمونید. اگر همه چیز رو درست انجام داده باشید، به راحتی تست اجرا میشه و خروجی رو می‌تونید توی قسمت Job log پروژه‌تون ببینید.دو حالت وجود داره، یا تست موفقیت آمیزه (سبز رنگ) و شما خوشحالید و یا تست ناموفق هست (قرمز رنگ) و باید یه فکری به حالش کنید. حالا دیگه می‌مونه اینکه مشکل از کجاس و چه‌جوری باید حلش کرد.تست موفقیت آمیزتست ناموفقبرای اینکه خروجی تست، غیر از بحث pass/fail، موضوعات دیگه رو هم دربر بگیره، باید یه سری نرم‌افزار و سرویس دیگه هم استفاده کنید. یکی از اون نرم‌افزار/سرویس‌ها اسمش coveralls هست که باهاش می‌تونید بحث code coverage (پوشش کد) رو انجام بدید به این معنی که تستی که اجرا کردید چه خطوطی از اصل کد رو دربر گرفته و تستشون کرده، چون بعضی اوقات ممکنه unreachable code path داشته باشید یعنی تست شما اون بخش از کد رو بهش نمیرسه که تست کنه، مثلا یک سری از exceptionها ممکنه اتفاق نیافته که البته تست اون‌ها هم راه حل داره که توضیحش بمونه برای بعد!برای اینکه gREST رو تست کنم در مجموع 32 تا تست نوشتم که هر کدوم بخشی از کد رو تست می‌کنه و مجموعاً این تست‌ها 78 درصد از کد رو تست می‌کنند.در نهایت از FOSSA برای بررسی تطابق مجوزهای نرم‌افزاری استفاده کردم که یک کتابخانه بود که تطابق نداشت و من با کتابخانه مشابهی عوضش کردم تا مشکل مجوز نرم‌افزاری وجود نداشته باشه.امیدوارم در این نوشته با کلیت موضوع که تست بود آشنا شده باشید و بتونید از این روش‌ها توی پروژه‌هاتون استفاده کنید. قصدم از نوشتن این مطلب، اصلا آموزش تست‌نویسی نبود بلکه می‌خواستم روند کلی کاری که انجام دادم رو تشریح کنم تا برای شما شفاف بشه که چطور یه نرم‌افزار تست میشه و روال کارش به چه شکلیه. و اگر از قبل می‌دونستید که چه عالی! اگر سوالی هم داشتید، در حد توان، درخدمتم.</description>
                <category>مصطفی مرادیان</category>
                <author>مصطفی مرادیان</author>
                <pubDate>Thu, 28 Jun 2018 00:24:34 +0430</pubDate>
            </item>
                    <item>
                <title>راه‌اندازی پراکسی برای داکر</title>
                <link>https://virgool.io/Software/registry-as-pull-through-cache-and-proxy-dwupugl6jkk1</link>
                <description>یکی از بزرگترین مشکلات ما توی این مدتی که داکر به وجود اومده اینه که دسترسی کشور ما و چند تا کشور دیگه رو به سرورهاش بسته و خوب علتش هم مشخصه و اگر تلاش کنید که داکر رو روی سرورتون داخل ایران نصب کنید به مشکلات زیادی میخورید که اگر تجربه کرده باشید، متوجه منظورم میشید.از این موضوع که بگذریم، هر کاری راه حلی داره و راه حل این کاری که من انجام دادم اینجوریه که یه سرور خصوصی ارزون قیمت توی خارج از کشور خریدم و یه داکر رجیستری روش بالا آوردم و با استفاده از letsencrypt هم یه certificate ساختم و عملا یه پراکسی برای داکر سرورهای داخل کشوری که دارم ایجاد کردم. توی این مطلب هم میخوام کاری که انجام دادم رو برای شما شرح بدم تا شما هم اگر این مشکل رو دارید، مشکلتون حل بشه.همین‌طور که می‌دونید یه نرم‌افزاری هست به اسم docker registry که میتونه برای شما یه مخزن image خصوصی ایجاد کنه که شما دیگه نیاز نداشته باشید به ازای هر بار دانلود هر image توی داکر روی هر سرور، کلی هزینه پهنای باند و ... بدید. یکی از روش‌هاش که مشکل بالا رو حل نمیکنه اینه که اون image رو دانلود کرده باشید یا خودتون روی سرورتون ساخته باشید و همون image رو روی سروری که نرم‌افزار docker registry هست به اصطلاح push کنید. و از اون به بعد برای دانلود از آدرس اون سرور استفاده کنید به جای سرور داکر هاب. این خیلی خوبه اما این مشکل رو حل نمی‌کنه که ما داخل کشور هستیم و نمی‌تونیم از سایت داکر هاب چیزی رو pull (یا همون دانلود) کنیم. استفاده از VPN و ... به سرورهای خارجی هم کار سختیه، چون با توجه به اتفاقات اخیر، همه چیز کُنده و خیلی از مواقع دانلود ناموفقی دارید و به هدفتون نمی‌رسید.اول یه سرور (VPS) تهیه کنید. من ترجیحم سیستم‌عامل دبیانه ولی با هر سیستم‌عامل لینوکسی که راحتید کار کنید (و نتیجه‌اش هم اینکه مثلا مسیر فایل‌هاتون تو سرورهای centos یا redhat رو خودتون باید کشف کنید و من فقط سیستم‌عامل‌های برمبنای دبیان رو مثال میزنم).بعدش به روشی که توی سایت داکر گفته شده، داکر رو روی اون سرور نصب کنید. برای اینکه کار رو راحت کنیم، docker-compose رو هم نصب کنید.یه دایرکتوری بسازید به اسم docker-registry-proxy-cache و داخل اون یه دایرکتوری به اسم config.یه فایلی هست که موقع راه‌اندازی registry باید وجود داشته باشه تا به registry بفهمونیم که میخوایم به‌صورت پراکسی عمل کنه:$ mkdir -p docker-registry-proxy-cache/config$ sudo docker run -it --rm --entrypoint cat registry:2 /etc/docker/registry/config.yml /home/user/docker-registry-proxy-cache/config/config.ymlاین دستور رو اجرا کنید تا تنظیمات registry پیش‌فرض رو براتون تو مسیری که میخواید، کپی می‌کنه.برای اینکه کاملا بفهمه، یه کار دیگه هم انجام بدید و این خطوط رو به انتهای فایل config.yml اضافه کنید:proxy:
  remoteurl: https://registry-1.docker.ioتو همون مسیر، یه فایل دیگه به اسم docker-compose.yml بسازید و اینو داخلش قرار بدید:version: &#039;3&#039;
services:
 registry:
 restart: always
 image: registry:latest
 ports:
      - 443:443
 volumes:
      - ./config:/etc/docker/registry:ro
      - ./data:/var/lib/registry:rw
 environment:
      - REGISTRY_HTTP_ADDR=0.0.0.0:443
      - REGISTRY_HTTP_TLS_CERTIFICATE=/etc/docker/registry/domain.crt
      - REGISTRY_HTTP_TLS_KEY=/etc/docker/registry/domain.keyحالا به یه certificate نیاز دارید تا بتونید بدون دردسر و امن کار کنید و اینکه داکر برای اینکه سرورتون ناامنه، غر نزنه. برای اینکار بهترین و ارزان‌ترین روش، استفاده از letsencrypt ئه. با این دستور letsencrypt رو نصب کنید:$ sudo apt-get install -y letsencryptیه نکته‌ای اینجا هست. یا یه دامنه از قبل ثبت کردید و این سرور رو روی یه A record روی DNS تنظیم کردید و قابل دسترسه، مثل registry.example.com یا اینکه میخواید از آی‌پی سرورتون استفاده کنید. چون letsencrypt اجازه نمیده روی آی‌پی certificate ازش درخواست کنید، می‌تونید از یه wildcard DNS استفاده کنید. من برای این کار از nip.io استفاده کردم. روش کارش هم خیلی ساده‌اس و کافیه آدرس آی‌پی سرورتون رو به این شکل وارد کنید و به یه A record تبدیلش کنید. فرض بر اینکه سرورتون آی‌پیش 8.8.8.8 هستش:8.8.8.8.nip.ioبرای تستش هم کافیه یه ping ساده بزنید.حالا می‌تونید با استفاده از certbot که همراه letsencrypt نصب می‌شه، این دستور رو اجرا کنید و یه certificate معتبر از letsencrypt دریافت کنید:# certbot certonly --standalone --preferred-challenges https -d 8.8.8.8.nip.ioتبریک میگم، certificate جدید توی مسیر زیر ذخیره شده، یه سری بهش بزنید:# cd /etc/letsencrypt/live/8.8.8.8.nip.io/یه سری فایل هست که ما به سه تاش نیاز داریم. این دستورات رو اجرا کنید تا certificate و private key شما توی مسیر دایرکتوری که بالا ساختید کپی بشه:# cp privkey.pem /home/user/docker-registry-proxy-cache/config/domain.key
# cat cert.pem chain.pem &gt; /home/user/docker-registry-proxy-cache/config/domain.crtحالا کافیه که این دستور رو توی مسیر دایرکتوری اجرا کنید تا پراکسی‌تون ساخته و اجرا بشه:# docker-compose up -dاگر همه چیز خوب پیش بره، یه docker registry ساختید و می‌تونید ازش استفاده کنید. برای اینکه سرورتون رو تست کنید کافیه یه مرورگر باز کنید و آدرس زیر رو داخلش وارد کنید:https://8.8.8.8.nip.io/v2/_catalogاین آدرس imageهایی هست که قراره بعداً دانلود بشه که الان خالیه.حالا میمونه سرور(های) داخلیمون که قراره از این پراکسی استفاده کنن. کافیه یه فایل تو مسیر زیر درست کنید و اطلاعات سرور رو داخلش اضافه کنید:# nano /etc/docker/daemon.json
{
    &amp;quotregistry-mirrors&amp;quot: [&amp;quothttps://8.8.8.8.nip.io&amp;quot]
}و در نهایت داکر رو ریستارت کنید و سعی کنید که یه image رو pull کنید:# service docker restart
# docker pull alpine:latestامیدوارم که همه چیز خوب پیش بره و موفق بشید تو این کار. اگر مشکلی یا سوالی داشتید، خوشحال میشم بپرسید و اگر بلد بودم حتما جوابتونو میدم.</description>
                <category>مصطفی مرادیان</category>
                <author>مصطفی مرادیان</author>
                <pubDate>Thu, 14 Jun 2018 04:09:31 +0430</pubDate>
            </item>
                    <item>
                <title>داستان gREST؛ گراف دیتابیس و Restful API</title>
                <link>https://virgool.io/apieco/grest-restful-api-development-framework-iv1p0gjgopcc</link>
                <description>بیشتر از یه سال پیش، دنبال یه فریم‌ورک میگشتم که بتونم باهاش سرویس‌های یه بک‌اند رو طراحی کنم و از دیتابیس Neo4j استفاده کنم (همین‌طور که می‌دونید یه دیتابیس برپایه‌ی گرافه). اینکه این فریم‌ورک آزادی سفارشی‌سازی رو هم بهم بده، خیلی برام مهم بود. زبان برنامه‌نویسی که بهش علاقه دارم و باهاش کار میکنم، پایتونه، پس سعی کردم دنبال فریم‌ورک‌های پایتون بگردم، از Django گرفته تا Flask و باقی فریم‌ورک‌ها.بزرگترین مشکل این بود که هیچ کدوم از این فریم‌ورک‌ها از دیتابیس‌های گراف به‌صورت پیش‌فرض استفاده نمی‌کردن و باید خودم همه کار رو انجام میدادم و برای راحتی هم از Object-Graph Mapper (OGM) استفاده می‌کردم تا بتونم CRUD روی دیتابیس Neo4j رو انجام بدم. این کار با دیتابیس‌های SQL و یه سری دیتابیس‌های خاص NoSQL مثل MongoDB خیلی راحته و فقط کافیه schema رو تعریف کنید و یه سری route اضافه کنید تا عین هلو، فریم‌ورک باقی کار رو انجام بده. فرض کنید باید روی هر Node و Relationship یه سری کد بزنی و همه چیز رو خودت تعریف کنی واسه هر کاری که قراره با استفاده از HTTP verbs انجام بشه.خلاصه کلنجار رفتن با فریم‌ورک‌های موجود این شد که تصمیم گرفتم خودم کلاس‌ها و متدها رو تعریف کنم تا بتونم viewهامو برای هر endpoint داشته باشم.چون با Flask تو پروژه‌های دیگه کار کرده بودم و خروجی قابل قبولی داشت برام، تصمیم گرفتم تو این کار هم از Flask استفاده کنم و از extensionهای موجودش هم استفاده کنم. برای اینکه بتونم viewهارو به شکل کلاس تعریف کنم از Flask-Classy استفاده کردم. بعد متوجه شدم که منسوخ شده پروژه‌اش و یکی دیگه پروژه رو fork گرفته، تغییر داده اسمشو و کلی هم pull request رو روش patch کرده و داره روش وقت میذاره. پس از Flask-Classful استفاده کردم.برای اینکه بتونم به قول مایکروسافتی‌ها schema-first پیش برم، از یه OGM استفاده کردم به اسم Neomodel که تقریبا تمام چیزهایی که من میخواستم برای ارتباط با Neo4j بهم میداد و syntax خیلی خوب و راحتی هم داشت به نسبت py2neo.از ترکیب Flask و Flask-Classful و Neomodel یه کلاس بزرگ نوشتم که همه کارهایی که من میخواستم با اون دیتابیس انجام بدم رو تسهیل میکرد، مثل ساخت Node و برقراری Relationship بین Nodeها. اون کلاسی که ساختم اسمشو گذاشتم generic_view و برای هر Node و Relationshipهاش از اون کلاس ارث‌بری میکردم و فقط اسم کلاس schema مرتبط با اون Node رو تو یه dictionary تعریف میکردم. این شد که برای هر endpoint نهایت به ده خط کد نیاز بود.همه چیز داشت خوب پیش میرفت که به ذهنم رسید که باید قرضمو به جامعه نرم‌افزارهای آزاد/متن‌باز باید پس بدم. به همین خاطر اون بخش از کد رو از پروژه کندم و تبدیلش کردم به یه پروژه جدید که بشه به عنوان یه extension توی Flask ازش استفاده کرد.و gREST متولد شد، فریم‌ورکی برای ساخت Restful API بر پایه‌ی پایتون با استفاده از Flask و Neo4j و Neomodel و رفقا! هدف اصلی راحت‌تر کردن توسعه نرم‌افزار و کاهش حجم کد بود.بعد از تولد تبدیلش کردم به یه package پایتون و روی PYPI منتشرش کردم. براش unit-test نوشتم، یه README و دو تا مثال ساده که توسعه‌دهنده‌ها بتونن بیشتر در موردش بدونن. اخیرا دارم روش مستندات کامل‌تر هم کار میکنم.برای unit-testها از pytest، برای CI از Travis و برای code coverage از Coveralls استفاده کردم. برای اینکه بتونم به توسعه‌دهنده‌ها کمک کنم و همچنین بتونم از کمکشون هم استفاده کنم از Gitter استفاده کردم. چون لایسنس پروژه برام خیلی مهم بود، از GPL v3 استفاده کردم و از سرویس ابری FOSSA برای چک کردن تطابق لایسنس فعلی با کتابخونه‌های استفاده شده تو پروژه استفاده کردم. تیمی که FOSSA رو ساخته واقعا قابل تشویق و تقدیره، کارشون حرف نداره. جالبه بدونید که یکی از کتابخونه‌هایی که استفاده میکردم تطابق لایسنسی نداشت و مجبور شدم عوضش کنم.الان که بیشتر از یک سال از انتشار این پروژه میگذره، بر مبنای کوئری که از BigQuery روی دیتابیس دانلود پروژه‌های PYPI گرفتم، بیش از 36000 بار این پروژه دانلود شده.تعداد دانلود پروژه gREST بر مبنای اطلاعات دیتاست the-psf:pypi با استفاده از BigQuery گوگلچون اون موقع که پروژه رو منتشر کردم در موردش توئیت کردم، بلاگ Neo4j در موردش یه خط نوشت!در نهایت، خیلی سعی کردم در طول این سال باگ‌های رو رفع کنم و بهبود بدم کد و قابلیت‌های پروژه رو اما به کمک جامعه نیاز دارم تا بتونم بیشتر توسعه بدم کار رو. ایده‌های دیگه‌ای هم برای زبان‌ها و فریم‌ورک‌های دیگه دارم. امیدوارم که این کار باعث تقویت و پیشرفت جامعه بشه. تو نوشته‌ی بعدیم در مورد نحوه کار و ساخت یه پروژه ساده بر مبنای gREST مطلب می‌نویسم.همین مطلب رو به زبان انگلیسی تو Medium هم قبلا منتشر کردم:https://medium.com/@mostafamoradian/the-story-of-grest-a-graph-base-restful-api-development-framework-e1f93b8a850d</description>
                <category>مصطفی مرادیان</category>
                <author>مصطفی مرادیان</author>
                <pubDate>Tue, 22 May 2018 12:52:51 +0430</pubDate>
            </item>
            </channel>
</rss>