Tutorial ini memberikan panduan komprehensif untuk memahami dan bekerja dengan Bahasa Khusus Domain (NammaDSL) di NammaYatri. Ini mencakup pembuatan file YAML, pembuatan kode, dan kompilasi, serta sintaksis untuk menentukan API dan penyimpanan.
Lokasi : Buat file YAML di dalam folder spec
modul yang sedang Anda kerjakan. Misalnya, jika Anda sedang mengerjakan rider-app
, jalurnya adalah:
rider-platform/rider-app/spec
Bergantung pada jenis spesifikasi yang Anda tetapkan, Anda dapat menempatkan file di dalam folder API
atau Storage
.
Pembuatan Kode : Setelah mendefinisikan file YAML, jalankan perintah berikut untuk menghasilkan kode:
, run-generator
Perintah ini menghasilkan file Haskell Beam, kueri, dan domain di direktori src-read-only
, serta kueri SQL.
Catatan Penting : Perintah ini hanya akan menghasilkan file spesifikasi yang baru atau diubah. Hal ini dilakukan dengan mendapatkan hash saat ini dari file spesifikasi dan membandingkannya dengan hash file dari komit HEAD
, run-generator --all
gunakan argumen "--all" untuk menghasilkan semua file spesifikasi
Kompilasi : Kompilasi kode menggunakan:
cabal build all
imports
: Digunakan untuk mengimpor tipe yang telah ditentukan sebelumnya.
importPackageOverrides
: Digunakan untuk mengganti paket impor Lihat Selengkapnya
module
: Menentukan nama modul.
Catatan: Dalam kasus API dasbor, semua API dalam modul memiliki awalan API yang sama, secara default module
dikonversi ke camel case.
apiPrefix
: menimpa awalan API default untuk API dasbor utama dan pembantu (opsional, khusus untuk dasbor).
Catatan: Nilai kosong ""
diperbolehkan untuk apiPrefix
dan helperApiPrefix
, artinya tidak ada awalan API.
helperApiPrefix
: menimpa awalan API untuk API dasbor pembantu (opsional, khusus untuk dasbor).
types
: Mendefinisikan jenis permintaan dan respons untuk API Anda. Bidang ini opsional. Sama seperti Tipe Kompleks di Storage DSL. Jenis didefinisikan dalam format berikut:
{TypeName}:
- {field1}: {field1Type}
- {field2}: {field2Type}
- derive: {Any extra derivation}
Catatan: Sintaks lama tidak mempertahankan urutan bidang dan tidak akan digunakan lagi.
{TypeName}:
{field1}: {field1Type}
{field2}: {field2Type}
derive: {Any extra derivation}
Tipe enum dapat didefinisikan sebagai:
{TypeName}:
- enum: {enum1},{enum2}
- derive: {Any extra derivation}
Untuk membuat tipe atau tipe baru sebagai pengganti data, gunakan recordType: NewType | Data (Default) | Type
{TypeName}:
- recordType: NewType
- {fieldName}: {fieldType}
- derive: {Any extra derivation}
{TypeName}:
- recordType: Type
- type: {fieldType}
Untuk membuat instance HideSecrets default, gunakan kata kunci derive
(khusus untuk dasbor)
{TypeName}:
- {fieldName}: {fieldType}
- derive: "'HideSecrets"
apis
: Berisi semua API dalam format berikut:
{httpMethod}
(metode HTTP DAPATKAN | POST | PUT | HAPUS) endpoint
: jalur API
/path1/path2/{pathParam1}/path3/{pathParam2}
Catatan: Jenis param jalur harus disebutkan di bagian params di bawah.
name
: nama API. Terkadang dua API berbeda memiliki nama API yang sama yang dihasilkan secara otomatis dari jalurnya, sehingga dapat ditimpa (opsional)
Catatan: Berhati-hatilah saat Anda mengubah apiName
untuk API yang sudah ada. Ketika apName
diubah, pembuatan Endpoint
dan UserActonType
juga akan diubah, data lama harus dimigrasikan sebagai berikut:
migrate:
endpoint: <oldEndpoint>
userActionType: <oldUserActionType>
Biasanya, <oldEndpoint>
dan <oldUserActionType>
merupakan nilai yang sama dalam format ini:
PROVIDER_MANAGEMENT/BOOKING/POST_BOOKING_CANCEL_ALL_STUCK
response
:
type
: Jenis respons request
:
type
: Jenis permintaan (opsional) multipart
:
type
: Jenis permintaan jika permintaan multibagian (opsional) auth
: Metode otentikasi (default: TokenAuth) Lihat Lainnya
query
: Daftar parameter kueri
- {queryParam1}: {queryParam1Type}
Catatan: Sintaks lama tidak mempertahankan urutan params dan tidak akan digunakan lagi.
{queryParam1}: {queryParam1Type}
mandatoryQuery
: Daftar parameter kueri wajib
- {mandatoryQueryParam1}: {mandatoryQueryParam1Type}
Catatan: Sintaks lama tidak mempertahankan urutan params dan tidak akan digunakan lagi.
{mandatoryQueryParam1}: {mandatoryQueryParam1Type}
params
: Daftar parameter jalur
{pathParam1}: {pathParam1Type}
{pathParam2}: {pathParam2Type}
headers
: Daftar header
headers:
- {header1}: {headerType1}
- {header2}: {headerType2}
helperApi
: Secara rekursif berisi API pembantu dasbor dalam format yang sama dengan API utama (opsional, khusus untuk dasbor)
validation
: Nama yang memenuhi syarat untuk fungsi validasi permintaan (opsional)
Contoh:
imports : {}
module : Sos
types :
SosRes :
- sosId : Id Sos
SosDetailsRes :
- sos : Maybe Sos
SosReq :
- flow : SosType
- rideId : Id Ride
SosUpdateReq :
- status : SosStatus
- comment : Maybe Text
apis :
# GET /sos/getDetails
- GET :
endpoint : /sos/getDetails/{rideId}
auth : TokenAuth
params :
rideId : Id Ride
response :
type : API.Types.UI.Sos.SosDetailsRes
# # POST /sos/{sosId}/status
- POST :
endpoint : /sos/{sosId}/status
auth : TokenAuth RIDER_TYPE
params :
sosId : Id Sos
request :
type : API.Types.UI.Sos.SosUpdateReq
response :
type : Kernel.Types.APISuccess.APISuccess
auth: ApiAuth DRIVER_OFFER_BPP_MANAGEMENT DRIVERS LIST
imports
: Digunakan untuk mengimpor tipe yang telah ditentukan sebelumnya. Lihat Lebih Banyak{dataTypeName}
: Menentukan nama modul. tableName
: Nama tabel opsional, Dibutuhkan Snake_case dari dataTypeName
jika tidak ditentukan.
fields
: Mencantumkan semua bidang tabel dengan tipe Haskell. Lihat Lebih Banyak
constraints
: PrimaryKey | Kunci Sekunder | BukanNull | PENINGKATAN OTOMATIS
importPackageOverrides
: Digunakan untuk mengganti paket impor Lihat Selengkapnya
types
: Tipe yang ditentukan pengguna, mirip dengan tipe API. Lihat Lebih Banyak
derives
: Override berasal dari tipe Data utama.
derives : " Show,Eq,Ord "
beamType
: Tipe berkas yang ditentukan pengguna untuk tipe data tertentu. Lihat Lebih Banyak
beamFields
: Ubah nama bidang berkas yang ditentukan pengguna atau gunakan jika Anda ingin memiliki sesuatu yang berbeda di sisi berkas. Lihat Lebih Banyak
beamInstance
: Kita dapat menyebutkan contoh beam yang kita perlukan menggunakan kolom ini Lihat Selengkapnya
sqlType
: Tipe sql yang ditentukan pengguna untuk suatu bidang. Lihat Lebih Banyak
default
: Nilai sql default untuk bidang, jika ada
fields :
scheduleTryTimes : ' [Int] '
tripCategory : Text
default :
tripCategory : " 'All' "
scheduleTryTimes : " '{1800, 900, 300}' "
queries
: Semua kueri Beam untuk tabel. Lihat Lebih Banyak
cachedQueries:
Kueri cache untuk tabel. Lihat Lebih Banyak
fromTType
: FromTType bidang, jika ada. Lihat Lebih Banyak
toTType
: ToTType bidang, jika berlaku. Lihat Lebih Banyak
excludedFields
: Ada beberapa bidang umum seperti merchantId, merchantOperatingCityId, createAt, dan updateAt yang ditambahkan secara otomatis ke tipe data. Untuk menghapusnya gunakan ini.
excludedFields :
- merchantOperatingCityId
- merchantId
extraIndexes
: Indeks tambahan apa pun Lihat Selengkapnya
extraOperations
: Operasi Ekstra Lihat Selengkapnya
Anda harus memberikan nama modul di impor
imports :
Merchant : Domain.Types.Merchant
FRFSSearch : Domain.Types.FRFSSearch
Catatan: Jenis umum ini diimpor secara otomatis, sehingga Anda dapat langsung menggunakannya tanpa mengimpor
Text -> Kernel.Prelude
Maybe -> Kernel.Prelude
Double -> Kernel.Prelude
TimeOfDay -> Kernel.Prelude
Day -> Data.Time.Calendar
Int -> Kernel.Prelude
Bool -> Kernel.Prelude
Id -> Kernel.Types.Id
ShortId -> Kernel.Types.Id
UTCTime -> Kernel.Prelude
Meters -> Kernel.Types.Common
HighPrecMeters -> Kernel.Types.Common
Kilometers -> Kernel.Types.Common
HighPrecMoney -> Kernel.Types.Common
Seconds -> Kernel.Types.Common
imports:
DataType1: Domain.Types.DataType1
Untuk mengubah paket:
importPackageOverrides:
Domain.Types.DataType1: dashboard-api
Impor yang dihasilkan di haskell:
import "dashboard-api" Domain.Types.DataType1
Kadang-kadang kita mungkin perlu melewati pengesampingan paket ketika kita membuat paket yang sama. Maka kita harus menentukan pemetaan paket di konfigurasi dhall
:
, _packageMapping =
[ { _1 = GeneratorType. API_TYPES , _2 = " dashboard-api " }
{ _1 = GeneratorType. SERVANT_API , _2 = " rider-app " }
]
Impor yang dihasilkan di haskell untuk API_TYPES
:
import "this" Domain.Types.DataType1
Impor yang dihasilkan di haskell untuk SERVANT_API
:
import "dashboard-api" Domain.Types.DataType1
Di bagian bidang, sebutkan nama bidang dan jenis Haskell yang sesuai
imports : {}
LmsModule :
tableName : lms_module
fields :
id : Id LmsModule
merchantOperatingCityId : Id MerchantOperatingCity
category : LmsCategory
createdAt : UTCTime
updatedAt : UTCTime
duration : Int
noOfVideos : Int
rank : Int
variant : Maybe Variant
moduleCompletionCriteria : ModuleCompletionCriteria
Untuk tipe bidang Sederhana (yang baru saja diimpor) akan disalin di sisi berkas juga kecuali kami menyebutkan Tipe Balok tertentu
Jika Bidang adalah Tipe Kompleks maka bidang tersebut akan dipecah secara rekursif di sisi berkas kecuali kami menyebutkan Tipe Balok Tertentu
Dalam hal Id, ShortId, Jenis Balok akan dianggap sebagai Teks
Jika kita ingin tipe Data yang Diimpor di sisi domain dan Id yang sesuai di sisi beam Anda, kami menggunakan ekstensi WithId dengan definisi tipe.
fields :
field1 : Int
beamType :
field1 : Text
fields :
a : Int
b : Text
beamFields :
a : " aa "
b : " bb "
# SomeType = SomeType {
# integerValueInText :: Text,
# version :: Int
# }
import :
SomeType : Domain.Types.SomeType
Some :
fields :
id : Id Some
val : SomeType # See this is an imported type
beamFields :
val :
intValue : Int
intValueInText : Text
# We have to right the toTType and fromTType functions
toTType :
intValue : (Kernel.Prelude.read . Domain.Types.SomeType.integerValueInText)
intValueInText : Domain.Types.SomeType.integerValueInText
fromTType :
val : mkVal
data Some = Some
{ id :: Kernel.Types.Id. Id Domain.Types.Some. Some ,
val :: Domain.Types.SomeType. SomeType ,
merchantId :: Kernel.Prelude. Maybe ( Kernel.Types.Id. Id Domain.Types.Merchant. Merchant ),
merchantOperatingCityId :: Kernel.Prelude. Maybe ( Kernel.Types.Id. Id Domain.Types.MerchantOperatingCity. MerchantOperatingCity ),
createdAt :: Kernel.Prelude. UTCTime ,
updatedAt :: Kernel.Prelude. UTCTime
}
deriving ( Generic , Show , ToJSON , FromJSON , ToSchema )
data SomeT f = SomeT
{ id :: B. C f Kernel.Prelude. Text ,
intValue :: B. C f Kernel.Prelude. Int ,
intValueInText :: B. C f Kernel.Prelude. Text ,
merchantId :: B. C f ( Kernel.Prelude. Maybe ( Kernel.Prelude. Text )),
merchantOperatingCityId :: B. C f ( Kernel.Prelude. Maybe ( Kernel.Prelude. Text )),
createdAt :: B. C f Kernel.Prelude. UTCTime ,
updatedAt :: B. C f Kernel.Prelude. UTCTime
}
deriving ( Generic , B.Beamable )
beamInstance : MakeTableInstances
$ (mkTableInstances ''PersonT " person " )
beamInstance : MakeTableInstancesGenericSchema
$ (mkTableInstancesGenericSchema ''PersonT " person " )
beamInstance : MakeTableInstancesWithTModifier [("deviceOS", "device_o_s")]
$ (mkTableInstancesWithTModifier ''MetaDataT " meta_data " [( " deviceOS " , " device_o_s " )])
beamInstance : Custom mkCacParseInstace [[Table2],[Table3]]
$ (mkCacParseInstace ''MetaDataT [[ Table2 ], [ Table3 ]])
beamInstance : Custom mkCacParseInstace "table_name" [Table2] [Table3] [(a,b,c)]
beamInstance :
- MakeTableInstances
- Custom mkCacParseInstace [[Table2],[Table3]]
- Custom Tool.Something.mkSomething "abc" [(a,b,c)] [[a],[b],[c]]
$ (mkTableInstances ''PersonT " person " )
$ (mkCacParseInstace ''MetaDataT [[ Table2 ], [ Table3 ]])
$ ( Tool.Something. mkSomething ''MetaDataT " abc " [(a,b,c)] [[a],[b],[c]])
fields :
field1 : " [Int] "
sqlType :
field1 : " text[] "
toTType :
subscriberUrl : Kernel.Prelude.func1|I
gatewayUrl : showBaseUrlSimple # This function will be created in seperated file as it's not imported
registryUrl : showBaseUrl|M # This function will be created in seperated file as it's not imported
fromTType :
updatedAt : Kernel.Prelude.fromMaybe createdAt|I
isScheduled : makeSchelude
tripCategory : Kernel.Prelude.fromMaybe (Domain.Types.Common.OneWay Domain.Types.Common.OneWayOnDemandDynamicOffer)|I
fields :
isVerified : Bool
verificationUrl : Text
aadharId : Text
toTType :
isVerified : (K.B.verify aadharId verificationUrl)|E
toTType :
isVerified : K.B.verify isVerified aadharId verificationUrl)|EM
toTType :
isVerified : K.B.verify isVerified (K.B.C.isCorrectAadhar aadharId) verificationUrl)|EM
WithId: Ini digunakan ketika kita ingin mengimpor Id tipe data sebagai Beam Field. Ini tidak membuat tipe data dalam kueri buat balok.
WithCachedId: Sama seperti WithId, satu-satunya perbedaan adalah kueri buat dan findbyId diimpor dari Storage.CachedQuery.*
WithIdCreate , WithCachedIdCreate: Gunakan ini saat diperlukan untuk membuat tipe data dalam kueri pembuatan. Penting: Tipe Data yang diimpor harus memiliki fungsi buat di file kueri yang sesuai. Contoh:
fields :
fareParams : Maybe FareParameters|WithIdCreate
farePolicy : Maybe FarePolicy|WithCachedId
Kueri pembuatan yang dihasilkan:
create :: ( EsqDBFlow m r , MonadFlow m , CacheFlow m r ) => Domain.Types.Estimate. Estimate -> m ()
create tbl = do
Kernel.Prelude. whenJust tbl . fareParams Storage.Queries.FareParameters. create
createWithKV tbl
Konversi ToTType dan FromTType yang dihasilkan:
instance FromTType' Beam. Estimate Domain.Types.Estimate. Estimate where
fromTType' Beam. EstimateT { .. } = do
fareParams' <- maybe ( pure Nothing ) ( Storage.Queries.FareParameters. findById . Kernel.Types.Id. Id ) fareParamsId
farePolicy' <- maybe ( pure Nothing ) ( Storage.CachedQueries.FarePolicy. findById . Kernel.Types.Id. Id ) farePolicyId
pure $
Just
Domain.Types.Estimate. Estimate
{
fareParams = fareParams',
farePolicy = farePolicy'
}
instance ToTType' Beam. Estimate Domain.Types.Estimate. Estimate where
toTType' Domain.Types.Estimate. Estimate { .. } = do
Beam. EstimateT
{ Beam. fareParamsId = ( Kernel.Types.Id. getId . ( . id ) <$> ) fareParams,
Beam. farePolicyId = ( Kernel.Types.Id. getId . ( . id ) <$> ) farePolicy
}
Sintaksis:
queries:
{query function name}:
kvFunction: {kv function name}
params: [field1, field2 .. ] Array of field to be updated in update queries
where:
{where clause}
orderby: {field name} (optional)
Sintaks Klausa Dimana:
where:
- {operator1}:
- field1
- {operator2}:
- field2
- field3
- {operator3}:
- field4
- field5
Bidang yang digunakan dalam params dan klausa Where dapat terdiri dari 3 jenis:
where:
not_eq
where:
not_eq:
- id: id2|B
myname|CS -> "myname"
123|CI -> 123
123|CS -> "123"
0.23|CD -> 0.23
"0.34"|CS -> "0.23"
true|CB -> True
Domain.Something.defaultValue|CIM -> Domain.Something.defaultValue (and Domain.Something is added to imports)
kvFunction: updateWithKV
params:
- status: newStatus
where:
eq:
- status: NEW|CIM
kvFunction: updateWithKV
params:
- status: Domain.Types.DataType.CONFIRMED|CIM
where:
eq:
- status: NEW|CIM
where:
and:
- eq:
- status: NEW|CIM
- id|B
Daftar operator tempat:
Contoh:
LmsModule :
fields :
id : Id LmsModule
category : LmsCategory
question : Question
field1 : Text
field2 : Text
field3 : Text
types :
LmsCategory :
enum : " Safety, Financial, Training "
QuestionType :
enum : " Maths, Physics, Chemistry "
derive : " Show "
Question :
question : Text
tp : QuestionType
derive : " Show,Eq "
queries :
findByComplexCondition :
kvFunction : findAllWithOptionsKV
where :
and :
- field1
- or :
- field2
- field3
- category
- question
orderBy : createdAt
updateQuestionById :
kvFunction : updateWithKV
params :
- question
where :
id
Kueri yang Dihasilkan:
findAllWithKV
[ Se. And
[ Se. Is Beam. field1 $ Se. Eq field1,
Se. Or
[ Se. Is Beam. field2 $ Se. Eq field2,
Se. Is Beam. field3 $ Se. Eq field3
],
Se. Is Beam. category $ Se. Eq category,
Se. Is Beam. questionQuestion $ Se. Eq $ Domain.Types.LmsModule. question question,
Se. Is Beam. questionTp $ Se. Eq $ Domain.Types.LmsModule. tp question
]
]
updateQuestionById question ( Kernel.Types.Id. Id id ) = do
_now <- getCurrentTime
updateWithKV
[ Se. Set Beam. questionQuestion $ Domain.Types.LmsModule. question question,
Se. Set Beam. questionTp $ Domain.Types.LmsModule. tp question,
Se. Set Beam. updatedAt _now
]
[ Se. Is Beam. id $ Se. Eq id
]