Ինչպե՞ս հաստատել, որ Protobuf հաղորդագրությունը չի պարունակում զրոյական արժեք ունեցող enum դաշտեր: Պարզվում է, որ սա ուղղակիորեն չի աջակցվում Protobuf-ի կողմից: Մենք պետք է նայենք, թե ինչպես է իրականացվում protojson փաթեթը:

Ավելի ու ավելի շատ ընկերություններ ընդունում են gRPC Protobuf-ով ներքին ծառայությունների միջև հաղորդակցության համար: Այն ունի բարձր կատարողականության առավելություններ, աջակցում է բազմաթիվ ծրագրավորման լեզուներին և աջակցում է Google-ին՝ շրջապատող հիանալի էկոհամակարգով:

Առջևի և արտաքին ծառայությունների հետ հաղորդակցվելու համար Protobuf-ը կարող է փոխակերպվել JSON ձևաչափի: Զննարկիչը հասկանում է միայն JSON ձևաչափը, և մենք չենք կարող ակնկալել, որ այլ ընկերություններ կսպառեն Protobuf-ը անմիջապես մեզանից: (Իհարկե, կարող ես, եթե բավականաչափ մեծ ես:)

Եկեք խոսենք enum-ի և որոշ խնդիրների մասին, որոնք մենք վերջերս բախվել ենք դրա հետ կապված:

Նմուշի կոդը գրված է Go-ում:

Protobuf ոճի ուղեցույցից զրոյական արժեք enum-ը պետք է ունենա UNSPECIFIED վերջածանց: Դա այն պատճառով է, որ enum-ն իրականացվում է որպես uint32, իսկ 0 արժեքը համարվում է չճշտված: Այն նման է nilին հաղորդագրության կամ դատարկ տողի համար: Protobuf-ը JSON-ով կոդավորելիս nil հաղորդագրությունը, UNSPECIFIED enum-ը կամ դատարկ տողը անտեսվում է:

Մենք մինչև մի օր հետևում էինք այդ կոնվենցիային, չէինք անում։

Արտաքին webhook հաղորդագրություններ ուղարկելիս մենք որոշեցինք չօգտագործել 0-ը որպես UNSPECIFIED: Պատճառներից մեկն այն է, որ մենք օգտագործում ենք EmitUnpopulated: true-ը, որպեսզի համոզվենք, որ բոլոր դաշտերը ներառված են JSON-ի ներկայացման մեջ՝ արտաքին կողմերին վեբ-կապիկ հաղորդագրություններ ուղարկելիս: Եվ մենք չենք ցանկանում, որ այդ UNSPECIFIED արժեքը հայտնվի webhook-ի հաղորդագրություններում, եթե ինչ-որ կերպ մոռանանք enum դաշտը դնել 0-ի: մենք՝ ինժեներներս, դա գիտենք:

Սա շատ դժվարություններ է առաջացնում, ուստի մենք ստիպված եղանք ետ վերադարձնել և 0 արժեքը կրկին դարձնել UNSPECIFIED: Խնդիրներից մեկն այն է, որ այն ստիպում է ամենուր օգտագործել EmitUnpopulated: true-ը: Եվ կան վայրեր, որտեղ մենք չենք ցանկանում արտանետել բոլոր չբնակեցված դաշտերը: Ինչպես զանգահարել որոշ երրորդ կողմի API-ներ: Որոշ հաղորդագրություններ խառնվում են UNSPECIFIED թվերի և ոչ UNSPECIFIED թվերի միջև; դրանով ճիշտ ձևաչափ ուղարկելու ուղիներ չկան: Օգտագործեք EmitUnpopulated: true, երրորդ կողմի API-ները չեն հասկանում UNSPECIFIED; օգտագործեք EmitUnpopulated: false և որոշ պարտադիր դաշտեր՝ ոչ UNSPECIFIED թվերով, բաց թողնված են: Իհարկե, դրանք բոլորը կարող են վերափոխվել, բայց սկզբում UNSPECIFIED-ի օգտագործումը պարտադրելը պետք է ավելի պարզ լինի:

Դե, ինչու՞ պարզապես չհաստատել, որ բոլոր թվային դաշտերը 0-ի վրա չեն դրված webhook հաղորդագրություններում: Դուք կարող եք հարցնել.

Պարզվեց, որ Protobuf 3-ում դա անելու պարզ ուղիներ չկան:

Protobuf 2-ում կա required տարբերակ՝ կանխելու դաշտի չեղարկումը: Այս տարբերակը հեռացվել է Protobuf 3-ում, քանի որ այն կանխում է վերամշակումը դաշտերը հեռացնելու համար: Եթե ​​մենք մոռանանք թարմացնել բոլոր ծառայությունները, որպեսզի հեռացնենք այդ չօգտագործվող required դաշտը, հատկապես մի ընկերությունում, որտեղ բազմաթիվ թիմեր են աշխատում, հաղորդագրություններն անգիտակցաբար կհեռացվեն: Ավելի լավ է դա նախապես չպահանջել: (ավելին)

Protobuf 3-ում կար jsonpb.JSONPBMarshaler ինտերֆեյս: Մենք կարող ենք պարզապես կիրառել այդ ինտերֆեյսը բոլոր թվերի համար, որպեսզի զրոյական արժեք տեսնելիս սխալ վերադարձնեն: Բայց նորից հանվեց։ Որպես արձանագրություն, մենք պետք է հնարավորինս նվազագույնի հասցնենք հարմարեցումը: Հակառակ դեպքում, այդ հարմարեցումը պետք է իրականացվի և պահպանվի բոլոր տարբեր լեզուներով տարբեր թիմերում:

Այսպիսով, ինչպե՞ս վավերացնել զրոյական արժեքը enum դաշտերում:

Մենք պետք է հասնենք արտացոլման փաթեթին. protoreflect.Message ինտերֆեյսն ունի Range() մեթոդ՝ յուրաքանչյուր բնակեցված դաշտի վրա կրկնելու համար: Մենք կարող ենք օգտագործել այդ մեթոդը՝ ստուգելու համար, որ զրոյական թվով դաշտեր չկան… Օ՜, սպասիր: Այն կրկնվում է միայն բնակեցված դաշտերի վրա: Այսպիսով, այն չի հայտնաբերի զրոյական արժեքը enum-ում:

Բայց protojson.Marshal() ֆունկցիան դեռ կարող է բաց թողնել չբնակեցված դաշտեր EmitUnpopulated տարբերակով: Ինչպե՞ս է դա իրականացնում: Սուզվեք encoding/protojson-ում, կա կոդի հատված՝ չբնակեցված դաշտերի (աղբյուր) կրկնելու համար: Եկեք գողանանք.

Այն, ինչ անում է վերը նշված կոդը, կրկնվում է լրացուցիչ դաշտերի վրա՝ շրջելով protoreflect.Message.Descriptor().Fields()-ի վրա: oneof դաշտերում գտնվող դաշտերը բաց են թողնվել: Չբնակեցված եզակի message դաշտերը սահմանվում են որպես invalid (մտածեք որպես null ստեղծված JSON-ում) նախքան մուտքագրման ֆունկցիա ուղարկելը:

Այդուհանդերձ, գրելու համար մի քիչ ավելի շատ կոդ, ինչպես օրինակ Protobuf-ի բոլոր տարբեր տեսակների վրա կրկնելու շրջագայական մեթոդի ներդրումը՝ հաղորդագրություն, զանգված (կրկնվող), դինամիկ Struct և, իհարկե, enum: Բայց դա լուծելի է. Եվ ես հիմա կարող եմ հանգստանալ:

Շնորհակալություն կարդալու համար:

Want to Connect?
Also published at my blog. Follow me on Twitter for more.