本教程提供了理解和使用 NammaYatri 中的領域特定語言 (NammaDSL) 的全面指南。它涵蓋了 YAML 檔案的建立、程式碼產生和編譯,以及定義 API 和儲存的語法。
位置:在您正在處理的模組的spec
資料夾中建立一個YAML 檔案。例如,如果您正在開發rider-app
,路徑將是:
rider-platform/rider-app/spec
根據您定義的規範類型,您可以將檔案放置在API
或Storage
資料夾中。
程式碼產生:定義YAML檔案後,執行以下命令產生程式碼:
, run-generator
此命令在src-read-only
目錄中產生 Haskell Beam、查詢和網域檔案以及 SQL 查詢。
重要提示:此命令將僅產生那些新的或更改的規範文件。這是透過取得規範檔案的當前雜湊值並與 HEAD 提交的檔案雜湊值進行比較來完成的
, run-generator --all
使用“--all”參數產生所有規範文件
編譯:使用以下命令編譯程式碼:
cabal build all
imports
:用於導入預定義類型。
importPackageOverrides
:用於覆蓋導入包 查看更多
module
:指定模組的名稱。
注意:對於儀表板 API,模組中的所有 API 都具有相同的 API 前綴,預設module
會轉換為駝峰式大小寫。
apiPrefix
:覆蓋主儀表板 API 和輔助儀表板 API 的預設 API 前綴(可選,特定於儀表板)。
注意: apiPrefix
和helperApiPrefix
允許使用空值""
,這意味著沒有 API 前綴。
helperApiPrefix
:覆寫助手儀表板 API 的 API 前綴(可選,特定於儀表板)。
types
:定義 API 的請求和回應類型。該字段是可選的。與儲存 DSL 中的複雜類型相同。類型按以下格式定義:
{TypeName}:
- {field1}: {field1Type}
- {field2}: {field2Type}
- derive: {Any extra derivation}
注意:舊語法不保留欄位順序,將被棄用。
{TypeName}:
{field1}: {field1Type}
{field2}: {field2Type}
derive: {Any extra derivation}
枚舉類型可以定義為:
{TypeName}:
- enum: {enum1},{enum2}
- derive: {Any extra derivation}
若要建立新類型或類型而不是數據,請使用recordType: NewType | Data (Default) | Type
{TypeName}:
- recordType: NewType
- {fieldName}: {fieldType}
- derive: {Any extra derivation}
{TypeName}:
- recordType: Type
- type: {fieldType}
若要建立預設的 HideSecrets 實例,請使用derive
關鍵字(特定於儀表板)
{TypeName}:
- {fieldName}: {fieldType}
- derive: "'HideSecrets"
apis
:包含以下格式的所有 API:
{httpMethod}
(HTTP 方法 GET | POST | PUT | DELETE) endpoint
:API路徑
/path1/path2/{pathParam1}/path3/{pathParam2}
注意:路徑參數類型應在下面的參數部分中提及。
name
:API 名稱。有時兩個不同的 API 具有從路徑自動產生的相同 API 名稱,因此可以覆寫它(可選)
注意:更改現有 API 的apiName
時要小心。當apName
更改時, Endpoint
和UserActonType
產生也會更改,舊資料應遷移如下:
migrate:
endpoint: <oldEndpoint>
userActionType: <oldUserActionType>
通常, <oldEndpoint>
和<oldUserActionType>
是以下格式的相同值:
PROVIDER_MANAGEMENT/BOOKING/POST_BOOKING_CANCEL_ALL_STUCK
response
:
type
:回應類型request
:
type
:請求類型(可選) multipart
:
type
:多部分請求時的請求類型(可選) auth
: 驗證方法(預設:TokenAuth) 查看更多
query
: 查詢參數列表
- {queryParam1}: {queryParam1Type}
注意:舊語法不保留參數順序,將被棄用。
{queryParam1}: {queryParam1Type}
mandatoryQuery
:強制查詢參數列表
- {mandatoryQueryParam1}: {mandatoryQueryParam1Type}
注意:舊語法不保留參數順序,將被棄用。
{mandatoryQueryParam1}: {mandatoryQueryParam1Type}
params
:路徑參數列表
{pathParam1}: {pathParam1Type}
{pathParam2}: {pathParam2Type}
headers
: 標題列表
headers:
- {header1}: {headerType1}
- {header2}: {headerType2}
helperApi
:遞歸地包含與主 API 格式相同的儀表板助理 API(可選,特定於儀表板)
validation
:請求驗證函數的限定名稱(可選)
例子:
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
:用於導入預定義類型。看更多{dataTypeName}
:指定模組的名稱。 tableName
:表格的可選名稱,如果未定義,則採用dataTypeName
的蛇形命名法。
fields
:列出 Haskell 類型表的所有欄位。看更多
constraints
:主鍵 |次要密鑰 |不為空 |自動增量
importPackageOverrides
:用於覆蓋導入包 查看更多
types
:使用者定義的類型,類似API類型。看更多
derives
:覆蓋主資料類型的派生。
derives : " Show,Eq,Ord "
beamType
:指定資料類型的使用者定義光束類型。看更多
beamFields
:使用者定義的光束欄位名稱,如果您想在光束側有不同的東西,請變更或使用它。看更多
beamInstance
:我們可以使用此欄位提及我們需要的梁實例 查看更多
sqlType
:欄位的使用者定義的 sql 類型。看更多
default
:欄位的預設 sql 值(如果有)
fields :
scheduleTryTimes : ' [Int] '
tripCategory : Text
default :
tripCategory : " 'All' "
scheduleTryTimes : " '{1800, 900, 300}' "
queries
:表的所有 Beam 查詢。看更多
cachedQueries:
表格的快取查詢。看更多
fromTType
:欄位的 FromTType(如果適用)。看更多
toTType
:欄位的 ToTType(如果適用)。看更多
excludedFields
:有一些常見字段,例如merchantId、merchantOperatingCityId、createdAt和updatedAt,這些字段是自動添加到資料類型中的。要刪除它們,請使用它。
excludedFields :
- merchantOperatingCityId
- merchantId
extraIndexes
: 任何附加索引 看更多
extraOperations
: 額外操作 看更多
您必須在導入中提供模組名稱
imports :
Merchant : Domain.Types.Merchant
FRFSSearch : Domain.Types.FRFSSearch
注意:這些常用類型是自動導入的,無需導入即可直接使用
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
更改套餐:
importPackageOverrides:
Domain.Types.DataType1: dashboard-api
在 haskell 中產生導入:
import "dashboard-api" Domain.Types.DataType1
有時,當我們在同一個包中生成時,我們可能需要跳過包覆蓋。然後我們應該在dhall
配置中指定套件映射:
, _packageMapping =
[ { _1 = GeneratorType. API_TYPES , _2 = " dashboard-api " }
{ _1 = GeneratorType. SERVANT_API , _2 = " rider-app " }
]
在 haskell 中為API_TYPES
產生導入:
import "this" Domain.Types.DataType1
在 haskell 中為SERVANT_API
產生導入:
import "dashboard-api" Domain.Types.DataType1
在欄位部分提及欄位名稱及其對應的 Haskell 類型
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
對於簡單字段類型(剛剛導入),除非我們提到特定的光束類型,否則它也會被複製到光束側
如果字段是複雜類型,那麼它將在光束側遞歸分割,除非我們提到特定的光束類型
如果是 Id、ShortId,Beam 類型將被視為文本
如果我們想要在域側匯入資料類型並在梁側使用對應的Id ,則我們可以使用 WithId 擴充功能和類型定義。
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:當我們想要匯入資料類型的 Id 作為 Beam Field 時使用。這不會在建立波束查詢中建立資料類型。
WithCachedId:與WithId相同,唯一的區別是create和findbyId查詢是從Storage.CachedQuery導入的。
WithIdCreate 、 WithCachedIdCreate:當需要在建立查詢中建立資料類型時使用此選項。重要提示:匯入的資料類型應在其對應的查詢文件中具有建立函數範例:
fields :
fareParams : Maybe FareParameters|WithIdCreate
farePolicy : Maybe FarePolicy|WithCachedId
產生的建立查詢:
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
產生的 ToTType 和 FromTType 轉換:
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
}
句法:
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)
Where 子句語法:
where:
- {operator1}:
- field1
- {operator2}:
- field2
- field3
- {operator3}:
- field4
- field5
params 和 where 子句中使用的欄位可以有 3 種類型:
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
where 運算子清單:
例子:
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
產生的查詢:
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
]