Ce didacticiel fournit un guide complet pour comprendre et utiliser le langage spécifique au domaine (NammaDSL) dans NammaYatri. Il couvre la création de fichiers YAML, la génération et la compilation de code, ainsi que la syntaxe de définition des API et du stockage.
Emplacement : Créez un fichier YAML dans le dossier spec
du module sur lequel vous travaillez. Par exemple, si vous travaillez sur l' rider-app
, le chemin serait :
rider-platform/rider-app/spec
Selon le type de spécification que vous définissez, vous pouvez placer le fichier dans le dossier API
ou Storage
.
Génération de code : Après avoir défini les fichiers YAML, exécutez la commande suivante pour générer le code :
, run-generator
Cette commande génère des fichiers Haskell Beam, des requêtes et des domaines dans le répertoire src-read-only
, ainsi que les requêtes SQL.
Remarque importante : cette commande générera uniquement les fichiers de spécifications nouveaux ou modifiés. Cela se fait en obtenant le hachage actuel du fichier de spécifications et en le comparant avec le hachage du fichier du commit HEAD.
, run-generator --all
utilisez les arguments "--all" pour générer tous les fichiers de spécifications
Compilation : Compilez le code en utilisant :
cabal build all
imports
: utilisé pour importer des types prédéfinis.
importPackageOverrides
: utilisé pour remplacer les packages d'importation. Voir plus
module
: Spécifie le nom du module.
Remarque : Dans le cas des API de tableau de bord, toutes les API du module ont le même préfixe d'API. Par défaut, il s'agit module
converti en boîtier Camel.
apiPrefix
: remplace le préfixe d'API par défaut pour l'API du tableau de bord principal et d'assistance (facultatif, spécifique au tableau de bord).
Remarque : valeur vide ""
autorisée pour apiPrefix
et helperApiPrefix
, cela signifie qu'il n'y a pas de préfixe API.
helperApiPrefix
: écraser le préfixe de l'API pour l'API du tableau de bord d'assistance (facultatif, spécifique au tableau de bord).
types
: définit les types de requête et de réponse pour vos API. Ce champ est facultatif. Identique aux types complexes dans le stockage DSL. Les types sont définis au format suivant :
{TypeName}:
- {field1}: {field1Type}
- {field2}: {field2Type}
- derive: {Any extra derivation}
Remarque : L'ancienne syntaxe ne préserve pas l'ordre des champs et sera obsolète.
{TypeName}:
{field1}: {field1Type}
{field2}: {field2Type}
derive: {Any extra derivation}
Les types d'énumération peuvent être définis comme :
{TypeName}:
- enum: {enum1},{enum2}
- derive: {Any extra derivation}
Pour créer un nouveau type ou un type à la place de données, utilisez recordType: NewType | Data (Default) | Type
{TypeName}:
- recordType: NewType
- {fieldName}: {fieldType}
- derive: {Any extra derivation}
{TypeName}:
- recordType: Type
- type: {fieldType}
Pour créer une instance HideSecrets par défaut, utilisez le mot-clé derive
(spécifique au tableau de bord)
{TypeName}:
- {fieldName}: {fieldType}
- derive: "'HideSecrets"
apis
: Contient toutes les API au format suivant :
{httpMethod}
(Méthode HTTP GET | POST | PUT | DELETE) endpoint
: chemin de l'API
/path1/path2/{pathParam1}/path3/{pathParam2}
Remarque : les types de paramètres de chemin doivent être mentionnés dans la partie paramètres ci-dessous.
name
: nom de l'API. Parfois, deux API différentes ont le même nom d'API généré automatiquement à partir du chemin, il peut donc être écrasé (facultatif)
Remarque : Soyez prudent lorsque vous modifiez apiName
pour une API déjà existante. Lorsque apName
a changé, la génération Endpoint
et UserActonType
sera également modifiée, les anciennes données doivent être migrées comme suit :
migrate:
endpoint: <oldEndpoint>
userActionType: <oldUserActionType>
Généralement, <oldEndpoint>
et <oldUserActionType>
sont les mêmes valeurs dans ce format :
PROVIDER_MANAGEMENT/BOOKING/POST_BOOKING_CANCEL_ALL_STUCK
response
:
type
: Type de réponse request
:
type
: Type de demande (facultatif) multipart
:
type
: Type de requête en cas de requête multipart (facultatif) auth
: Méthode d'authentification (par défaut : TokenAuth) Voir plus
query
: Liste des paramètres de requête
- {queryParam1}: {queryParam1Type}
Remarque : L'ancienne syntaxe ne préserve pas l'ordre des paramètres et sera obsolète.
{queryParam1}: {queryParam1Type}
mandatoryQuery
: Liste des paramètres de requête obligatoires
- {mandatoryQueryParam1}: {mandatoryQueryParam1Type}
Remarque : L'ancienne syntaxe ne préserve pas l'ordre des paramètres et sera obsolète.
{mandatoryQueryParam1}: {mandatoryQueryParam1Type}
params
: Liste des paramètres du chemin
{pathParam1}: {pathParam1Type}
{pathParam2}: {pathParam2Type}
headers
: Liste des en-têtes
headers:
- {header1}: {headerType1}
- {header2}: {headerType2}
helperApi
: contient de manière récursive l'API d'assistance du tableau de bord dans le même format que l'API principale (facultatif, spécifique au tableau de bord)
validation
: Nom qualifié pour la fonction de validation des requêtes (facultatif)
Exemple:
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
: utilisé pour importer des types prédéfinis. Voir plus{dataTypeName}
: Spécifie le nom du module. tableName
: Nom facultatif de la table, il prend le Snake_case du dataTypeName
s'il n'est pas défini.
fields
: Répertorie tous les champs de la table de type Haskell. Voir plus
constraints
: PrimaryKey | Clé secondaire | NonNull | AUTO-INCRÉMENT
importPackageOverrides
: utilisé pour remplacer les packages d'importation. Voir plus
types
: types définis par l'utilisateur, similaires aux types API. Voir plus
derives
: remplace les dérivés du type de données principal.
derives : " Show,Eq,Ord "
beamType
: type de faisceau défini par l'utilisateur pour un type de données spécifié. Voir plus
beamFields
: modifiez le nom du champ de faisceau défini par l'utilisateur ou utilisez-le si vous souhaitez avoir quelque chose de différent côté faisceau. Voir plus
beamInstance
: Nous pouvons mentionner l'instance de poutre dont nous avons besoin en utilisant ce champ. Voir plus
sqlType
: Type SQL défini par l'utilisateur pour un champ. Voir plus
default
: valeur SQL par défaut pour les champs, le cas échéant
fields :
scheduleTryTimes : ' [Int] '
tripCategory : Text
default :
tripCategory : " 'All' "
scheduleTryTimes : " '{1800, 900, 300}' "
queries
: Toutes les requêtes Beam pour la table. Voir plus
cachedQueries:
requêtes mises en cache pour la table. Voir plus
fromTType
: FromTType des champs, le cas échéant. Voir plus
toTType
: ToTType des champs, le cas échéant. Voir plus
excludedFields
: certains champs courants tels que MerchantId, MerchantOperatingCityId, CreateAt et UpdateAt sont automatiquement ajoutés au type de données. Pour les supprimer, utilisez ceci.
excludedFields :
- merchantOperatingCityId
- merchantId
extraIndexes
: Tous les index supplémentaires Voir plus
extraOperations
: Opérations supplémentaires Voir plus
Vous devez fournir le nom du module dans les importations
imports :
Merchant : Domain.Types.Merchant
FRFSSearch : Domain.Types.FRFSSearch
Remarque : Ces types courants sont importés automatiquement, vous pouvez donc les utiliser directement sans les importer.
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
Pour changer de forfait :
importPackageOverrides:
Domain.Types.DataType1: dashboard-api
Importation générée en Haskell :
import "dashboard-api" Domain.Types.DataType1
Parfois, nous devrons ignorer le remplacement du package lorsque nous générons dans le même package. Ensuite, nous devrions spécifier le mappage des packages dans les configurations dhall
:
, _packageMapping =
[ { _1 = GeneratorType. API_TYPES , _2 = " dashboard-api " }
{ _1 = GeneratorType. SERVANT_API , _2 = " rider-app " }
]
Import généré en haskell pour API_TYPES
:
import "this" Domain.Types.DataType1
Import généré en haskell pour SERVANT_API
:
import "dashboard-api" Domain.Types.DataType1
Dans la section champ, mentionnez le nom du champ et le type Haskell correspondant
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
Pour les types de champs simples (qui viennent d'être importés), il sera également copié du côté de la poutre, sauf si nous mentionnons un type de poutre spécifique.
Si le champ est de type complexe, il sera divisé de manière récursive du côté du faisceau, sauf si nous mentionnons un type de faisceau spécifique.
En cas d'Id, ShortId, le type de faisceau sera considéré comme du texte
Si nous voulons le type de données importées côté domaine et l'identifiant correspondant côté faisceau, nous utilisons les extensions WithId avec la définition de type.
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 : Ceci est utilisé lorsque nous voulons l'ID du type de données importé comme champ de faisceau. Cela ne crée pas le type de données dans la requête de création de poutre.
WithCachedId : identique à WithId, la seule différence est que les requêtes create et findbyId sont importées de Storage.CachedQuery.*
WithIdCreate , WithCachedIdCreate : utilisez-le lorsque cela est nécessaire pour créer le type de données dans la requête de création. Important : Le type de données importé doit avoir une fonction de création dans son fichier de requête correspondant. Exemple :
fields :
fareParams : Maybe FareParameters|WithIdCreate
farePolicy : Maybe FarePolicy|WithCachedId
Requête de création générée :
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
Conversions ToTType et FromTType générées :
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
}
Syntaxe:
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)
Syntaxe de la clause Where :
where:
- {operator1}:
- field1
- {operator2}:
- field2
- field3
- {operator3}:
- field4
- field5
Les champs utilisés dans les paramètres et la clause Where peuvent être de 3 types :
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
Liste des opérateurs Where :
Exemple:
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
Requête générée :
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
]