full_stack_101 تقييم صلاحية بيانات التطبيق واستخدام المدقق ESLint


ابراهيم الخضور

علينا في الواقع، أن نطبق مجموعة من القيود على البيانات التي ستخزن في قاعدة بيانات التطبيق. فلا ينبغي أن نسمح بوجود ملاحظات مفقودة المحتوى أو لا تملك الخاصية content لسبب ما. يجري تفقد صلاحية الملاحظة من خلال معالج مسار:

app.post('/api/notes', (request, response) => {
  const body = request.body
  if (body.content === undefined)
  {
      return response.status(400).json({ error: 'content missing' })
  }
  // ...
})

إن لم تمتلك الملاحظة الخاصية content، سيستجيب الخادم برمز الحالة 400 (طلب خاطئ).

يمكننا أن نستخدم طريقة أفضل في تقييم تنسيق البيانات قبل تخزينها في قاعدة البيانات وهي الوظيفة validation التي تتيحها مكتبة Mongoose. حيث سنعرف معايير تقييم محددة لكل حقل من حقول مخطط قاعدة البيانات:

const noteSchema = new mongoose.Schema({
  content:
    {
        type: String,
        minlength: 5,
        required: true
    },
    date:
    {
        type: Date,
        required: true
    },
    important: Boolean
})

تحدد القواعد السابقة أن يكون طول المحتوى 5 محارف على الأقل، وأن المحتوى أمر إجباري required:true وبالتالي يجب أن لا يكون مفقودّا، وكذلك التاريخ. لم نضف تقييدات على حقل الأهمية، فلم يتغير تعريفه في المخطط.

لاحظ على سبيل المثال أن المقيّم minlength يأتي مدمجًا وجاهزًا للاستخدام مع المكتبة mongoose، لكنها أيضًا تقدم وظيفة المُقيِّمات الخاصة التي تمكننا من إنشاء مُقيِّمات بالطريقة التي نحتاجها إن لم تحقق المقّيمات المدمجة ما نريد. إن حاولنا أن نخزن كائنًا في قاعدة البيانات يخالف أحد التقييدات التي فرضناها عليه، سترمي العملية استثناءً.

لنغيّر معالج إنشاء ملاحظة جديدة لكي يمرر أي استثناء محتمل إلى الأداة الوسطية لمعالجة الأخطاء:

app.post('/api/notes', (request, response, next) => {
  const body = request.body

  const note = new Note({
    content: body.content,
    important: body.important || false,
    date: new Date(),
  })

  note.save()
    .then(savedNote => {
      response.json(savedNote.toJSON())
    })
    .catch(error => next(error))})

لنوسّع معالج الخطأ ليتعامل مع أخطاء التقييم.

const errorHandler = (error, request, response, next) => {
  console.error(error.message)

  if (error.name === 'CastError') {
    return response.status(400).send({ error: 'malformatted id' })
  }
 else if (error.name === 'ValidationError') {
    return response.status(400).json({ error: error.message })
  }

  next(error)
}

عندما يُخفق تقييم الكائن، ستعيد Mongoose رسالة الخطأ التالية:

mongo_valid_error_001.png

سلاسل الوعود

تحوّل معظم معالجات المسار البيانات القادمة مع الاستجابة إلى الصيغة الصحيحة باستخدام التابع toJSON. فعندما ننشئ ملاحظة جديدة، يُستدعى هذا التابع ليعالج الكائن الذي مرر إلى التابع then:

app.post('/api/notes', (request, response, next) => {
  // ...

  note.save()
    .then(savedNote => {
      response.json(savedNote.toJSON())
    })
    .catch(error => next(error)) 
})

يمكن أن نحصل على نفس النتيجة وبطريقة أكثر وضوحًا باستخدام سلاسل الوعود:

app.post('/api/notes', (request, response, next) => {
  // ...

  note
    .save()
    .then(savedNote => {
      return savedNote.toJSON()
  })
      .then(savedAndFormattedNote => {
      response.json(savedAndFormattedNote)
   })
      .catch(error => next(error)) 
})

يستقبل أول تابع then الكائن savedNote الذي تعيده Mongoose ثم ينسقه، ويعيد نتيجة هذه العملية. وكما ناقشنا سابقًا يعيد تابع then الذي يليه وعدًا أيضًا بحيث يمكننا الوصول إلى الملاحظة المنسقة بالتصريح عن دالة استدعاء جديدة داخل التابع then الأخير. كما يمكننا توضيح الشيفرة أكثر باستخدام الصيغة المختصرة للدالة السهمية:

app.post('/api/notes', (request, response, next) => {
  // ...

  note
    .save()
    .then(savedNote => savedNote.toJSON())
    .then(savedAndFormattedNote => {
      response.json(savedAndFormattedNote)
    }) 
    .catch(error => next(error)) 
})

لم تحقق سلسلة الوعود في هذا المثال الكثير من الفائدة. لكن الفائدة الحقيقة ستظهر عند تنفيذ عدة عمليات غير متزامنة على التسلسل. لن نخوض في هذا الموضوع كثيرًا، بل سنتركه إلى القسم التالي من المنهاج، حيث سنطلع على الصيغة async/await في JavaScript والتي تسهل كتابة العمليات المتسلسلة غير المتزامنة.

إنجاز نسخة الإنتاج من الواجهة الخلفية المرتبطة بقاعدة بيانات

ينبغي أن يعمل التطبيق كما هو على Heroku. ولا ينبغي أن ننشئ نسخة إنتاج جديدة للواجهة الأمامية نتيجة للتغيرات التي أجريناها عليها. ونشير أيضًا إلى أن استخدام متغيرات البيئة التي عرفناها باستخدام dotenv غير ممكن عندما تكون الواجهة الخلفية في وضع الإنتاج (على Heroku).

لقد وضعنا متغيرات البيئة المستخدمة في مرحلة التطوير في الملف env.، لكن ينبغي ضبط متغيرات البيئة التي تعرف عنوان موقع قاعدة البيانات في وضع الإنتاج باستخدام الأمر heroku config:set

$ heroku config:set MONGODB_URI=mongodb+srv://fullstack:secretpasswordhere@cluster0-ostce.mongodb.net/note-app?retryWrites=true

ملاحظة: إن أعطى تنفيذ الأمر خطأً، ضع قيمة MONGO_URI ضمن إشارتي تنصيص مفردتين (' ').

$ heroku config:set MONGODB_URI='mongodb+srv://fullstack:secretpasswordhere@cluster0-ostce.mongodb.net/note-app?retryWrites=true'

ينبغي أن يعمل التطبيق الآن. لكن في بعض الأحيان لا تجري الأمور كما هو مخطط لها. لذلك استفد من سجلات heroku عند تنفيذ الشيفرة. توضح الصورة التالية ما أظهره Heroku عند فشل التطبيق بعد إجراء التغييرات:

heroku_code_fail_002.png

لسبب ما، ظهر عنوان قاعدة البيانات ككائن غير معرّف. لكن سجلات Heroku قد كشفت أن الخطأ هو أننا أسندنا عنوان موقع قاعدة البيانات إلى متغير البيئة MONGO_URL بينما تتوقع الشيفرة أن يكون العنوان قد أسند إلى متغير البيئة MONGODB_URI.

ستجد شيفرة التطبيق بوضعه الحالي في الفرع part3-5 ضمن المستودع الخاص بالقسم على GitHub

التمارين 3.19 - 3.21

3.19 دليل هاتف بقاعدة بيانات: الخطوة 7

ضع مقيّمات لتحديد صلاحية المُدخَلات إلى تطبيق دليل الهاتف، لتضمن أن المُدخَل الجديد يحمل اسمًا فريدًا غير مكرر. لن تسمح الواجهة الأمامية الآن أن يدخل المستخدم أسماء مكررة، لكن حاول أن تقوم بذلك مباشرة بوجود Postman أو VS Code REST client.

لا تقدم Mongoose مُقيِّمات مدمجة لهذا الغرض، عليك تثبيت حزمة mongoose-unique-validator باستخدام npm. إن حاول طلب HTTP-POST إضافة اسم موجود مسبقًا، على الخادم أن يجيب برمز الحالة المناسب، بالإضافة إلى رسالة خطأ.

ملاحظة: سيسبب المعرف الفريد (unique-validator) تحذيرًا سيطبع على الطرفية.

(node:49251) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.
connected to MongoDB

اقرأ توثيق Mongoose وجد طريقة للتخلص من هذا التحذير.

3.20 دليل هاتف بقاعدة بيانات: الخطوة 8 *

وسع التقييد بحيث لا يقل طول الاسم المخزّن في قاعدة البيانات عن ثلاثة محارف وأن لا يقل طول رقم الهاتف عن 8 أرقام. دع الواجهة الأمامية تظهر رسالة خطأ عندما يحدث خطأ في التقييم. يمكنك أن تعالج الخطأ بإضافة كتلة catch كما يلي:

personService
    .create({ ... })
    .then(createdPerson => {
      // ...
    })
    .catch(error => {
      // هذه طريقة للوصول إلى رسالة الخطأ
      console.log(error.response.data)
    })

يمكنك إظهار رسالة الخطأ الافتراضية التي تعيدها Mongoose علمًا أن قراءتها ليست يسيرة:

mongoose_def_error_003.png

ملاحظة: أثناء عمليات التحديث لن تعمل مقّيمات Mongoose بشكل افتراضي. اطلع على التوثيق لتتعلم كيفية تمكينها.

3.21 إنجاز نسخة الإنتاج من الواجهة الخلفية المرتبطة بقاعدة بيانات

أنشئ نسخة كاملة (full stack) من التطبيق بإنجاز نسخة إنتاج عن الواجهة الأمامية ونسخها إلى مستودع الواجهة الخلفية. تحقق من أن كل شيء يعمل جيدًا باستخدام التطبيق بشكله الكامل على الخادم المحلي الذي عنوانه https://localhost:3001. انقل النسخة النهائية إلى خادم Heroku وتحقق أن كل شيء يعمل بشكل جيد.

المدققات (Lints)

قبل أن ننتقل إلى القسم التالي من المنهاج، سنلقي نظرة على أداة مهمة تدعى المدقق lint. وجاء في wikipedia عن المدقق ما يلي:

اقتباس

بشكل عام، المدقق هو أية أداة قادرة على التقاط الأخطاء والإشارة إليها في لغات البرمجة، بما فيها أخطاء التنسيق. ويشير المصطلح "سلوك مشابه للمدقق" أحيانًا إلى عملية الإشارة إلى الاستخدام المريب للغة البرمجة. تقدم الأدوات الشبيهة بالمدققات تحليلًا ساكنًا للشيفرة المصدرية (تحليل الشيفرة دون تنفيذها)

يمكن للغات التي تترجم بشكل ساكن كلغة Java وبيئات التطوير مثل NetBeans أن تشير إلى الأخطاء في الشيفرة، حتى تلك الأخطاء التي تعتبر أكثر من أخطاء ترجمة. يمكن استعمال أدوات إضافية لتقوم بالتحليل الساكن مثل checkstyle لتوسيع إمكانيات بيئة التطوير بحيث تصبح قادرةً على الإشارة إلى مشاكل تتعلق حتى بالتنسيقات مثل إزاحة الكلمات ضمن الأسطر.

في عالم JavaScript، تعتبر الأداة ESlint هي الرائدة في مجال التدقيق والتحليل الساكن. لنثبت ESlint كملف ارتباط تطوير في مشروع الواجهة الخلفية بتنفيذ الأمر:

npm install eslint --save-dev

يمكننا بعد ذلك تهيئة المدقق بصيغته الافتراضية بتنفيذ الأمر:

node_modules/.bin/eslint --init

سنجيب طبعُا عن الأسئلة التالية:

eslint_config_004.png

ستخزّن إعدادات التهيئة في الملف eslinterc.js:

module.exports = {
    'env': {
        'commonjs': true,
        'es6': true,
        'node': true
    },
    'extends': 'eslint:recommended',
    'globals': {
        'Atomics': 'readonly',
        'SharedArrayBuffer': 'readonly'
    },
    'parserOptions': {
        'ecmaVersion': 2018
    },
    'rules': {
        'indent': [
            'error',
            4
        ],
        'linebreak-style': [
            'error',
            'unix'
        ],
        'quotes': [
            'error',
            'single'
        ],
        'semi': [
            'error',
            'never'
        ]
    }
}

لنغيّر مباشرة القاعدة التي تنظم الإزاحة في السطر الواحد بحيث تكون بمقدار فراغين:

"indent": [
    "error",
    2
],

يمكن تفتيش الملفات مثل index.js والتحقق من صلاحيتها باستخدام الأمر:

node_modules/.bin/eslint index.js

يفضل أن تنشئ سكربت npm منفصل للتدقيق:

{
  // ...
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    // ...
    "lint": "eslint ."
  },
  // ...
}

سيتحقق الآن الأمر npm run lint من كل ملف في المشروع. كما سيتحقق من الملفات الموجودة في المجلد build، وهذا ما لا نريده. لذلك سنمنع ذلك بإنشاء ملف تجاهل لاحقته eslintignorr.‎ في جذر المشروع و نزوده بالمحتوى التالي:

build

عندها لن يتحقق ESlint من المجلد build أو محتوياته.

سيشير ESlint إلى الكثير من النقاط في شيفرتك:

eslint_error_list_005.png

لن نصلح أي شيء الآن. يمكن أن تستخدم طريقة أفضل من سطر الأوامر في تنفيذ التدقيق، وهي تهيئة إضافة للتدقيق eslint-plugin على محررك بحيث تنفذ عملية التدقيق بشكل مستمر. وبالتالي سترى الأخطاء التي ترتكب مباشرة أثناء تحريرك للشيفرة.

يمكنك الاطلاع أكثر من خلال الانترنت على الكثير من المعلومات حول الإضافة Visual Studio ESlint plugin. Editor.

ستضع الإضافة السابقة خطًا أحمر تحت أخطاء التنسيق:

vs_eslint_style_error_006.png

 

وهكذا سنرصد الأخطاء بشكل أسهل.

للمدقق ESlint الكثير من القواعد سهلة الاستخدام والتي يمكن إضافتها في الملف eslintrc.js. لنضع الآن القاعدة eqeqeq التي تحذرنا إن وجدت في الشيفرة عامل الموازنة الثلاثي (===). تضاف القاعدة ضمن الحقل rules في ملف التهيئة:

{
  // ...
  'rules': {
    // ...
   'eqeqeq': 'error',
  },
}

وطالما أننا استخدمنا القواعد لنقم بتغييرات أخرى. لنمنع وجود المسافات الفارغة trailing spaces في آخر السطر البرمجي، ولنطلب أيضًا أن يكون هناك فراغ قبل وبعد الأقواس المعقوصة، كذلك وجود فراغ بشكل دائم بين معاملات الدالة السهمية.

{
  // ...
  'rules': {
    // ...
    'eqeqeq': 'error',
    'no-trailing-spaces': 'error',
    'object-curly-spacing': [
        'error', 'always'
    ],
    'arrow-spacing': [
        'error', { 'before': true, 'after': true }
    ]
  },
}

وتقدم لنا الإعدادات الافتراضية التي اعتمدناها في البداية، الكثير من القواعد التي ينصح بها المدقق، وتُطبق هذه القواعد بكتابة الأمر:

'extends': 'eslint:recommended',

تتضمن هذه القواعد تحذيرات تتعلق بأمر الطباعة على الطرفية console.log.

يمكن تعطيل أي قاعدة بجعل قيمتها تساوي 0 في ملف التهيئة. لنلغ القاعدة no-console على سبيل المثال:

{
  // ...
  'rules': {
    // ...
    'eqeqeq': 'error',
    'no-trailing-spaces': 'error',
    'object-curly-spacing': [
        'error', 'always'
    ],
    'arrow-spacing': [
        'error', { 'before': true, 'after': true }
    ],
    'no-console': 0
  },
}

ملاحظة: يفضل، إن أجريت أية تغييرات على الملف ‎.eslintrc.js، أن تشغل المدقق من سطر الأوامر لتتحقق من أن التعليمات في ملف التهيئة مكتوبة بالشكل الصحيح.

lint_config_chk_007.png

فإن كانت هناك أية مشاكل في ملف التهيئة، ستتصرف إضافة المدقق بشكل غير مفهوم.

تحدد الكثير من الشركات معايير لكتابة الشيفرة وتفرضها على منظمة W3C عبر ملفات تهيئة ESlint. إذًا ليس عليك إعادة اختراع العجلة في كل مرة، ومن الأفضل لك اعتماد ملف تهيئة جاهز أنجزته جهة ما.

تعتمد الكثير من المشاريع حاليًا على دليل تنسيق JavaScript الذي قدمته Airbnb، وذلك باستخدامها ملف تهيئة ESlint الذي تعتمده Airbnb.

ستجد شيفرة التطبيق كاملًا في الفرع part3-6 ضمن المستودع الخاص بالقسم على GitHub

التمرين 3.22

3.22 تهيئة المدقق

أضف ESlint إلى تطبيقك وأصلح كل المشاكل.

وهكذا نصل إلى آخر تمرينات هذا القسم، وحان الوقت لتسليم حلول التمارين إلى GitHub. لا تنس أن تشير إلى التمارين التي تسلمها أنها مكتملة ضمن منظومة تسليم التمارين.

ترجمة -وبتصرف- للفصل Validation and ESlint من سلسلة Deep Dive Into Modern Web Development





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن