Галоўная / Артыкулы

Анаіз прымаса-гарда Shapes: права на власнасць, проекцыя і контракты з запісу

Навучыцеся аналізаваць форматы Prisma-Guard як контракты API, запытваючы, хто ўладае кожным значэнням, якія даныя можна выдаць у адпаведзе, і якіы генераваны канцэнтры можна запускаць.

Выдавец: REACTAPP.TOP12 хв чытання2744 слоў

Крыніцы: Define Every Prisma Endpoint’s Inputs and Outputs in One Place

Язык аргументаў Prisma є выразны, і самэ гэта як раз і трэба ў надзеянам коде сервера. Аднак калі тая ж структура аргументаў застаёцца прыйнятай через HTTP, кожная можлівасць гэтага языка ператвараецца на рашэнне па аспектам продукту: што можа напісаць вызывач, якія фільтры ён можа выбраць, што сервер павінен зафіксаваць, якія зв’язкі можна вернуць, і сколькі дзецячых рэядоў можа містыць адпаведны адказ. Функцыі prisma-guard дазволяюць запісваць гэтыя адказы безпосередна пад самай операцыей Prisma. У гэтым кяліку показана, як рассматраць такую структуру як компактны контракт для кантролю, фільтрацыі, проекцыі і мутацыі, выкорыстоўваючы тры пытанні, якія можна задаць для будзь-яго pull request, які дадае новы канцэнтр.

Чаму структура ўсё, кроме простай верыфікацыі

Форма дзеяўчы гэтакі вхідны данны, але яе большая перадчыннае — гэта ўсуненне рашэнняў пра межы ў адным месцы. Генераваная операцыя можа спакою павязвацца з формай, у той час калі кожны працоўнік не павінен вручную перзбудаваць схемы вхідных дадзеных і спісы дозволеных поль. Гэта усувае повтараючыся код і таксама рызык таго, што два працоўніка будуць пасляпэўна адхіляцца ў тым, што яны дазволяюць.

Ценай за гэтую зручнасць ёсць точнасць. Форма являецца надзеяным контрактом толькі тады, калі яна адпавядае таму, што на самаёй бібліятэцы ўжо застосовваецца, і гэта включае чыстасць ў паведамленні пра тое, дзе заканчываецца дэйство высшага рэнтнера. Паведамлення, прыведзены нижэй, адбіваюцца ў складзе рэалізацыі, якую яе адпрацоўвальнікі тэставалі ў момент напісання, а не як часть усталёнай стандарты спяльноты, таму паказваныя деталі трэба падтвердзіць за дапамою дасягледжэнняў да вашай версіі. Якщо запит, які захаваны, вялікі ўжо не работае і вы хочаце з’ясавіць прычыну, пачніце з нашага модэлю дыагностикі неудач prisma-guard на адной з фаз.

Першы вопыт: хто ўладае кожным значэннем?

Кожны элемент формы перадае значэнне або кліенту, або серверу. Словнар змарак; у прыкладзе показаны ўсе чатыры формы па боку ад боку.

const values = {
  clientChooses: true,
  serverChooses: 'published',
  serverChoosesTrue: force(true),
  clientChoosesWithinRule: (base) => base.min(1).max(120),
}

Аккуратна чытанне лістоў:

  • Чыстае true не ўважаецца логічным значэнням. Це сігнал, які означае "выбірае кліент".
  • Літерал, такі як 'published', належыць серверу.
  • force(value) таксама належыць серверу. Яго патрэбна, калі значэнне, якое вы хочаце зафіксаваць, є літеральнаրо true, адколі чыстае true будзе спрыявацца як сігнал кліента.
  • Функцыя прыкручэння залишае рашэнне кліенту, але заменяе створаную для поля ланцоўку Zod на правілу, якое вы запісваеце безпосередна.

Размесцаванне вплывае на значэння функціі force

Словы простыя; сама ўстановка іх месца дзіўі людзей. У форме where прымусовы скаляр падае ўнутрь об’екта аператара. Фільтр падзеяў нижэй фіксуе статус публікацыі, адночасна дазваляючы кліенту шукать заглавы:

const eventShape = {
  where: {
    isPublished: { equals: force(true) },
    title: { contains: true },
  },
}

Калі ўсё скалярнае прэдыкатаў у корані where ёст кароткаванае, бібліятэка стаўится да вхідных дадзеных кліента ў гэтым поле па-мягкаму. Незалежна ад таго, чы робіць выклікач тое жа значэнне, надсылае іншае чы дадае які-небудзь іншы аператар, прэдыкат сервера мае пераканальнае права, і запит не будзе адхілён проста за тое, што у ям упамінана гэтае поле. Гэта не тое ж самае, што і стандартна настройка. Стандартная настройка заступаецца, калі кліент надае якісь дадзеныя; прымусовая настройка ніколі не заступаецца.

У всіх іншых випадках правіла є строгімі. Уявіце сабойны модыфікатор, які знаходзіцца паляў кантролюемага кліентам оператора, закрепленая польцо ў фільтры сувязей, закреплены фільтр у вкладанай структуре або закрепленая значэнне ў дадзеннях мутацыі. У всіх этых случаях ключ зникае з схемы, па якой адбываецца верыфікацыя кліента, таму ўсылкі з такім ключом не праходзяць верыфікацыю, нават якщо значэнне абсалютна зусім такое ж.

Гэта мае значэнне для коду кліента. Кліенту можа знадобіцца цэлыя адмовіцца ад польца, якое належыць серверу. «Выправленне» його на значэнне сервера таксама не дапамагае, таму што пераканаленне стосуецца таго, хто ўладае ключам, а не таго, чы равныя значэнні.

Стандартныя настройкі, обрабоцка асобых ситуацый і дапраўкі таксама перакладзяюць права на власнасць

Значэнні за замовчаннем і механізмы перадпрыёма заслугваюць аднаковага разгляду. У полі, якое кантролюецца кліентам, значэнне за замовчаннем парадоксальна пры ўтрате значэння застаёцца у своем нормальным стане, а механізм перадпрыёма можа заменіць некоректны вхідны даны на альтернатыўныя, замест таго каб адхіліць запит. Функцыі стварэння дадаюць додатковую сложнасць: дырэктыва за замовчаннем або перадпрыёма, створаная генератаром, можа нават запаўніць обавязковае поле, пра якое формат ніколі не згадваецца, і значэнне, яке ёна стварае, пасля чаго вводзіцца так, нібы сервер яго задаў. Таму месца размешчэння значэння за замовчаннем вялікае значэнне для таго, хто будзе володзець гэтым значэннем, таму павяржыцеся толькі на замену, калі цього трэбуе контракт API.

Даць дапраўненнія магчымая сховаць адпаведную пастку. Яны не расшырююць створаную ланцоўку у сваём вузле; яны ёю заменяюць. Таму ваша функцыя павінна занова пераказаць усе абмежэнні, на якіх залежыць канец-пункт, а не толькі дадатковыя мінімумы чы максімумы, якія вы хацелі дадаць. Гэта ўсё болей важліва, калі тип Prisma є менш строгім, чым правіло добутку, напрыклад, калі мова йде пра столбец з рэчамі, які ніколі не можа быць порожнім.

Прыводзеныя апдэйты і прыводзеныя дыс’юнкціі

Даныя апдэйту, якія зафіксаваны, запішываюцца праз кожны падтрымваны вызов. Падазроўцем, што апдэйт выканае формат, який прыводзіць да status: 'active': будзь-які апдэйт через гэты формат, нават той, які вырашае толькі змяніць заглавле, таксама скасоўвае статус. Якщо операцыя павінна застаўіць які-небудзь поле без змян, не включайце гэты поле ў формат.

Булевая структура дадзе яшчэ адну правілу. Абавязковая умова, якая размешчваецца пад OR, пераводзіцца ў абавязковую умову на самым верхнім рэвэле. У результате форма не можа выражыць справжняя правілу, прыналежнае серверу, у формате „альбо“, такую як записы, якія належаць гэтаму абঞ্চу, альбо тые, якія былі разданы з гэтай групой. Такія правілы выклікаюць патрэбу ў запытай, напісанай на сервере, у адной столбцу для вызначэння дыяпазону, який фіксуе правіла, або ў ўжыцці заходаў у самай базе дадзеных.

Таму першы вопыт для адзінаковага аналізу є болей конкрэтным, чым „Чы гэта падтверджана?“ Запытайце, хто вяршыць рашэнне па кожной значэнні, дзе размешчваецца кожная абавязковасць, і якая структура аргументаў на самай справе падае да Prisma.

Другі вопыт: што можа адпаведзь раскрываць?

Фільтрацыя і проекцыя адпаведзяют на разныя запитанні. Фільтр арендніка выбірае, якія записы падходзяць. Проекцыя выбірае, якія з іх полей і асоціяцый трэба вернуць. Патрэбны і тое, і другое, а перагляд аднаго нічога не сказвае пра другой.

Для захаваных чытанняў select або include, заявленыя на структуре, ўодночас являюцца спискам дазволенага і альтэрнатываюцым варыянтом. Кліенты можаць запрашваць болей сузаную структуру, але не больш нічога, і калі яны не прысылаюць проекцыю, захаваны дэлегат выкарыстоўвае заявленую.

Структура квітанцыі, паказаная нижэй, дазволяе кліенту запрашваць квітанцыю па ідэнтыфікатору, вяртае фіксаваны набор корэневых полей і адкрывае вялізны список прымечанняў кліента з сае фільтрам, полямі і розмерам сторынкі.

const ticketShape = {
  where: {
    id: { equals: true },
  },
  select: {
    id: true,
    subject: true,
    description: true,
    customerNotes: {
      where: {
        accountId: { equals: force(accountId) },
      },
      select: {
        id: true,
        body: true,
      },
      take: { max: 20, default: 10 },
    },
  },
}

Этая структура выканае чатыры окремыя задачы:

  • Корэневыя поля, якія не пераказаны, застаюцца недоступнымі.
  • Можна вярнуць толькі пераказаныя поля прымечанняў.
  • Запісі завжды залишаюцца толькі ў акаунтэ вызывача.
  • У аднай адпаведзі можа быць не больш 20 запісей, а якщо кліент нічога не зазначыў — 10.
  • Вяроўнаныя з вялікім колькасцю элементаў не падпадаюць під аўтаматычны дыапазон

    Фільтр запісей спецыяльна створаны так, каб не працаваць з вялікім колькасцю элементаў. Аўтаматычны дыапазон прызначаны толькі для операцый, якія падтрымваюцца ў коране запиту; ён не распрастараецца на вяроўнаныя, якія запрашваюцца за дапамогою select або include, і не дадае там додатковых умоў. Не трэба забываць пра вяроўнаныя з where, адночаса квітанцыя з правильным дыапазонам все равно можа вярнуць запісы, якія належаць іншаму акаунту.

    У зносах типу „дзе-многі“ дапрацоўваецца вярстаны where, ўласная проекцыя і межа рэядка. У зносах типу „многі-адзін“ нельга выкарыстоўваць вярстаны Prisma where, таму трэба іншая стратэгія: адмовіцца ад такога зносу, прадасты толькі безнебяжныя скалярныя поля з яго, завантажыць яго через уласны захіщаны запит на роўні корэню або захаваць яго за дапамой правіл базы дадзеных.

    Выбор стылю проекцыі

    Выберачы форму проекцыі па тым, як яна будзе дзейсніцца па меры росту схемы:

    • За дапамою select столбец, які пазней дадаўся да модэлю, застаецца схованым, пакуль хтось яго не пераглядзіць і не уключыць, таму гэты варыянт ёсць найбезпечнейшым для публічных канцэнтраў.
    • За дапамою include вы отрымваеце зносы, але корэневы модэль все рава вяртае ўсі своі скалярныя столбцы, таму чутлівыя даны корэню застаюцца виднымі.
  • За дапамою omit вы стварыцее спіс заборон, і тое, чы гэты спіс ў доступе, залежыць ад вашага генератора і версіі Prisma.
  • Гэтыя ўмоўкі являюцься альтернатывамі, а не роўнамі. Ніколі не сумешчайте select, include і omit на аднам роўні дрэва.

    Пярасчыты, вкладаныя стандартныя значэння і адпаведзі на мутацыі

    Пярасчыт колькасці асоціяцыі ёст тэрпэчны вузол проекцыі, а не статыстыка, прыўязаная да асоціяцыі, яку вы завантажылі. Якщо асоціяцыя типу «до-многа», якая вяртаецца, фільтруецца па абліку, то ў елементе _count павінен быць той самы фільтр, калі кліенты вырашаюць, што обе цифры описваюць тыя ж рэкорды. Теставайце умову стварання асоціяцыі і умову пярасчыту незалежна.

    Стандартныя настройкі звёрнутай проекцыі вылучаюцца з адзяванай структуры. Якщо кліент прасіць про адзяваныя зв’язкі з выкарыстоўваннем краткага формата Boolean, такія зв’язкі можаюць застаўсяць з своім фільтрам і обмежэнням па сторунцы. Аднак правыя, якія надаюцца кліенту, не заполняюць відсутнія варыянты: дазвол на сортаванне па польі не задае напрамку, які кліент не выказаў. У тэстах трэба пераканацца ў трох варыянтах — пропусканні корэнтной проекцыі, явнай звёрнутай проекцыі і краткам формате зв’язкаў, а не прыпускать, што перакананне ў однам варыянцы паводзіць да пераканання ў іншых.

    Мутацыі намеравана ўсасиметрычныя. Форма мутацыі можа пераканацца ў проекцыі, якую прасіць кліент, але як толькі параметр enforceProjection не ўвімкнуты, кліент, який нічога не прасіць, не отрымае проекцыі у заданай форме, і дзейніча звычная практыка Prisma. Увімкніце гэты параметр тады, калі адпаведзі на мутацыі павінны быць обмежаныя адзяванымі польямі.

    У падзеці, пераканайцеся, што кожны тэст выконвае тую фазу, яю ён прызначан пераблікаваць. Выклік guard.query(...).parse() — гарны спосаб пераканацца ў наявнасці фільтраў чытання і меж, але значэнні параметраў праекцыі чытання паказваюцца толькі пасля выконання захоўванага дэлегата, а праекцыя мутацыі таксама выклікаецца толькі праз захоўваны шлях мутацыі.

    Таму другы вопыт — не «ці є select?»; запытайце, якія корэневыя поля, супаўзеленыя поля, супаўзеленыя рядкі і колькасці можа прымець кожны варіант вызывача.

    Трэці вопыт: якія запісы можна выконваць?

    Створаныя канцэнты запісу прыймаюць аргументы у саўстанні Prisma. Формат дадзеных вялічын наказвае, якія значэнні падае кліент, якія — сервер, і чы можлівы якія-небудзь вёсканыя аперацыі.

    Форматы стварэння должны урахоўваць кожнае неабходнае поле

    Формат стварэння адхіляецца, калі якое-небудзь неабходнае поле не мае джерела. Допусканыя джэрела включаюць:

    • кліент, калі поля пазначаецца як кераваное кліентам
    • сэрвер, праз літэрал або force
    • стандартны значэнне, заданае ў схеме Prisma
    • стандартны значэнне або механізм падтрымленае прыемнікам Zod
    • вбрыск зместу стэкса для знаковай кіяўцы тэнанта
    • дазволены запіс аб’екта, які запоўняе ёю

    Прыклад падпіску дазволяе кліенту выбраць зовнішню кіяўцу і план, обмежвае колькасць месцаў правілам цэлага числа, фіксуе пачатковы статус і версію на сэрверы, а таксама вяртае скарыгаваны набор дадзеных.

    const createSubscriptionShape = {
      data: {
        externalKey: (base) => base.min(1).max(100),
        planId: true,
        status: 'trialing',
        seatCount: (base) => base.int().min(1).max(500),
        version: 0,
      },
      select: {
        id: true,
        externalKey: true,
        status: true,
        seatCount: true,
      },
    }
    

    Зверніце ўвагу на тое, чаго няма: знаковай кіяўцы тэнанта. Яна ніколі не павинна быць чымось, што кліент задае пад час стварэння. Корант стэкса выходзіць з надзеянага контекста запыту, а расширэнне сама дадае адпаведную знаковую кіяўцу для запісаў на роўні коранта, якія ён падтрымляе.

    Масовыя операцыі і стварэнне групава

    Большыя мутацыі должны працаваць пасля двух пераканальнікаў. Першы — updateMany, яго рэзультаты і deleteMany выклікаюць патрабаванне аб заданні умовы where. Другі — запрошаны запит павінен ствараць рэальную умову. Абсэнцыя тэла запиту, порожняе об’екта аператара чы ўмова, якая падходзіць да всего, не падпадаюць пад адзынаковасць, што захоўвае атлас ад нехтарактальных змян у всій таблеце.

    Функцыя batch з адзынаковасцю выклікае патрабаванне аб масэвым об’екте і не працуе з адзіным об’ектам без паведамлення. Тое, чы робяцься масэвыя операцыі і чы функцыі, такія як прахід заўтрэйных дадзеных, ўсё яшчэ залежыць ад вашага прадавца базы дадзеных.

    Upsert — гэта тры контракты

    Формат upsert на самай працэ ўскладнены з трох форматаў: унікальны селектар, пакет дадзеных для стварэння і пакет дадзеных для апдэйта.

    const upsertSubscriptionShape = {
      where: {
        externalKey: true,
      },
      create: {
        externalKey: true,
        planId: true,
        status: 'trialing',
        seatCount: true,
        version: 0,
      },
      update: {
        planId: true,
        seatCount: true,
      },
    }
    

    У пакетах дадзеных, які ствароўваюць новыя елементы, неабходна уключыць усі обавязковыя поля, тады калі ў пакетах дадзеных для адчыненыя існуючых елементаў можна прыховаць будзь-што. Параметр create задае значэння толькі пад час дагаджання новай строкі, а параметр update заменяе яго ўсё час, калі існуючая строка падходзіць па умовам. У прыкладзе статус і версія задаюцься пад час ствароўнення і навмысна не уключаюцца пад час адчыненыя, таму існуючыя падпіскі застаюцься такімі, якія ў яных є.

    Унікальныя змены выканаюцца за дапамою стандартнага формата унікальных селектараў Prisma. Об’екты оператораў викорыстоўваюцца для звычайнага фільтраўвання і не маюць месца ў структуре унікальных умов where на роўні корэню.

    Контракты запісу і вярнення даных у зв’язках

    У документацыі даўальніка паказана неабходнасць прыняць окольныя рашэнні ў паўнасці власнасці. Форма може дазволіць выкананне методу connect, абмежыць клавішы-селектары, якія можа викорыстоўваць кліент, і адхіліць будзь-яе внутраняе выконанне, якое не ўказана. Аднак нічга з гэтага не распрастраняе сферу дасканалення на селектар. Калі важліва якая саме запис будзе падключана, ваш код аплікэцыі чы рэгістры должны пераканацца, што аберучы мае право на яе викорыстанне.

    Контракты адпаведзення таксама разніцяюцца залежна ад методу. Методы пакетнай обработкі, якія вяртаюць толькі колькасць — createMany, updateMany і deleteMany — адпавядаюць з пакетам даных, які містыць толькі калькуляцыю, і адхіляюць проекціі. Їхнія аналогі, якія вяртаюць даны, могу прыймать проекціі, якщо Prisma і ваш даўальнік гэта дазваляюць. Якщо настройка генератора пераключае маршрут на метод адпаведзення, неабходна пераглянуць новы адпаведзень як змяну API, а не ігнараваць яе як деталь рэалізацыі.

    Трэція запытанне — не «Чы гэты метод увёрнут?» Задаюць пытанне пра тое, што дае можлівасця гэты метод: поля дадзеных, формы селектара, умовы для большога колічэння элементаў, аперацияй з вазносамі і поля адпаведзення.

    Размешчэнне механізмаў адбавлення

    Форматы працуюць наякшэ, калі іх меры адбавлення задаюцца разам з іншымі механізмамі. Кожны слой мае свою задачу:

    • Аутентыфікованы запит прадае контэкст, якому можна даверыць.
    • Картаўкі дыяпазонаў дадаюць умовы корыстніка да падтрымваных аперацияў на роўні корэна.
    • Варыянты вызывача выбіраюць, які заявлёны формат паслужыць для данай аудытарыі.
    • Хукі выканаюць частковыя перакананні прыложэння, якія могу быць адхілены.
    • Рукапісныя працоўнікі керуюць запытамі, якія форматы не можу описаць.
    • Палітіка базы дадзеных заходзіць інварыянты, якія павінны застацца незменнымі праз будзь-які шлях доступу.

    Ніхто з іх не паважаецца выконваць чужую работу. Якщо вы вирашыце, якія маршруты створыць за дапамою алгорытму, а якія запісаць вручную, наша стаття пад збалансаванне стварэння Prisma CRUD і цялеспрямованага карантину маршрутаў рассказвае пра такое раздзеленне.

    Аптака перагляду і чэк-ліст для тэставання

    Калі запрос на падключэнне дадае створаны канцэнтры, перагляньце яго, задаючы сабе тры пытанні:

    1. Хто вяршыць выбіранне кожнага значэння, якое можа прыймаць канцэнтры?
    2. Якія поля і зв’язкі можа мець адпаведны адказ?
    3. Якія мутацыі і формы селектараў можа выканаць той, хто запрашвае данні?

    Тады кожны тэст павінен быць спрямованы на конкрэтную тэзу, яку ён павінен падтвердзіць. Аналізуйце аргументы чытання, каб параболізаваць прыменшэнне працы фільтраў. Запускайце захаваныя дэлегаты, каб параболізаваць праекты і запісы. Адглядайце шлях расшырэння, каб параболізаваць впрыск дыямэнту. Выбірайце фікстуры базы дадзеных з мнагамяўчыкамі, калі тэза стосуецца таго, якія рядкі вяртаюцца, а для тэстаў HTTP — калі яна стосуецца транспорту чы суставленых маршрутаў.

    Надаюць роўную важарнасць негатывным тэстам. Для кожнай аудытарыі прабуйце прачытаць корневае поле, якое не ўключанае у спіс, прачытаць чутлівае вкладзенаяе поле, перасягнуць межы зв’язку і запустіць операцыю запісу, якая забораная за форматам. Пазитывныя тэсты падтверджаюць, што запрошаны запит успехуе; негатывныя тэсты падтверджаюць, што межы застаюцца закрытымі пасля змян у модэлі чы генераторы.

    Заключанне

    Форма атрыбута заслуговае на названне „contract“ толькі тады, калі чыткая як тое, што ён дазволяе, так і тое, чаго не можа прымусіць. Адзельна пераглядка прав на власнасць, проекцыі і запісаў дазволяе рана выявіць типовыя прасочыні: слабкія сілы на роўні корэню, необмежаныя вкладзеныя адносы, пільнікі, якія ігнаруюць фільтры адносоў, і з’яєднанні, якія айначаюць межы тэнанта. Калі гэтыя прасочыні будуць прызначаны спецыяльным механізмам, працоўнікам або правілам базы дадзеных, гэнераванне коду можа пазбавіцься від павтаральнага коду, які керуе межамі, не ператвараючы язык запитоў Prisma на публічны API, які ніхто не меў на мету публікуваць. Для абсолютнай сынтаксі, точных паведамленняў пра адказы, повадкі, спецыфічныя для версій, і лабараторных тэстаў, якія можна перадаць, адзірніце практычныя вядомасці prisma-generator-express.