Este tutorial proporciona una guía completa para comprender y trabajar con el lenguaje específico de dominio (NammaDSL) en NammaYatri. Cubre la creación de archivos YAML, la generación y compilación de código, así como la sintaxis para definir API y almacenamiento.
Ubicación : cree un archivo YAML dentro de la carpeta spec
del módulo en el que está trabajando. Por ejemplo, si estás trabajando en la rider-app
, la ruta sería:
rider-platform/rider-app/spec
Dependiendo del tipo de especificación que esté definiendo, puede colocar el archivo dentro de la carpeta API
o Storage
.
Generación de código : después de definir los archivos YAML, ejecute el siguiente comando para generar el código:
, run-generator
Este comando genera archivos Haskell Beam, de consulta y de dominio en el directorio src-read-only
, así como las consultas SQL.
Nota importante : este comando solo generará aquellos archivos de especificaciones que sean nuevos o modificados. Esto se hace obteniendo el hash actual del archivo de especificaciones y comparándolo con el hash del archivo de la confirmación HEAD.
, run-generator --all
use los argumentos "--all" para generar todos los archivos de especificaciones
Compilación : compila el código usando:
cabal build all
imports
: Se utiliza para importar tipos predefinidos.
importPackageOverrides
: se utiliza para anular paquetes de importación Ver más
module
: especifica el nombre del módulo.
Nota: En el caso de las API del panel, todas las API del módulo tienen el mismo prefijo de API; de forma predeterminada, module
se convierte al formato camel.
apiPrefix
: sobrescribe el prefijo de API predeterminado para la API del panel principal y auxiliar (opcional, específico para el panel).
Nota: Se permite el valor vacío ""
para apiPrefix
y helperApiPrefix
, significa que no hay prefijo API.
helperApiPrefix
: sobrescribe el prefijo de API para la API del panel auxiliar (opcional, específico para el panel).
types
: define los tipos de solicitud y respuesta para sus API. Este campo es opcional. Igual que los tipos complejos en Storage DSL. Los tipos se definen en el siguiente formato:
{TypeName}:
- {field1}: {field1Type}
- {field2}: {field2Type}
- derive: {Any extra derivation}
Nota: La sintaxis anterior no conserva el orden de los campos y quedará obsoleta.
{TypeName}:
{field1}: {field1Type}
{field2}: {field2Type}
derive: {Any extra derivation}
Los tipos de enumeración se pueden definir como:
{TypeName}:
- enum: {enum1},{enum2}
- derive: {Any extra derivation}
Para crear un nuevo tipo o tipo en lugar de datos, utilice recordType: NewType | Data (Default) | Type
{TypeName}:
- recordType: NewType
- {fieldName}: {fieldType}
- derive: {Any extra derivation}
{TypeName}:
- recordType: Type
- type: {fieldType}
Para crear una instancia predeterminada de HideSecrets, utilice la palabra clave derive
(específica para el panel de control)
{TypeName}:
- {fieldName}: {fieldType}
- derive: "'HideSecrets"
apis
: contiene todas las API en el siguiente formato:
{httpMethod}
(método HTTP GET | POST | PUT | DELETE) endpoint
: ruta API
/path1/path2/{pathParam1}/path3/{pathParam2}
Nota: Los tipos de parámetros de ruta deben mencionarse en la parte de parámetros a continuación.
name
: nombre de API. A veces, dos API diferentes tienen el mismo nombre de API generado automáticamente a partir de la ruta, por lo que se puede sobrescribir (opcional)
Nota: Tenga cuidado al cambiar apiName
para una API ya existente. Cuando se cambia apName
, la generación de Endpoint
y UserActonType
también se cambiará, los datos antiguos se deben migrar de la siguiente manera:
migrate:
endpoint: <oldEndpoint>
userActionType: <oldUserActionType>
Normalmente, <oldEndpoint>
y <oldUserActionType>
son los mismos valores en este formato:
PROVIDER_MANAGEMENT/BOOKING/POST_BOOKING_CANCEL_ALL_STUCK
response
:
type
: Tipo de respuesta request
:
type
: Tipo de solicitud (opcional) multipart
:
type
: Tipo de solicitud en caso de solicitud de varias partes (opcional) auth
: método de autenticación (predeterminado: TokenAuth) Ver más
query
: Lista de parámetros de consulta
- {queryParam1}: {queryParam1Type}
Nota: La sintaxis anterior no conserva el orden de los parámetros y quedará obsoleta.
{queryParam1}: {queryParam1Type}
mandatoryQuery
: Lista de parámetros de consulta obligatorios
- {mandatoryQueryParam1}: {mandatoryQueryParam1Type}
Nota: La sintaxis anterior no conserva el orden de los parámetros y quedará obsoleta.
{mandatoryQueryParam1}: {mandatoryQueryParam1Type}
params
: Lista de parámetros de ruta
{pathParam1}: {pathParam1Type}
{pathParam2}: {pathParam2Type}
headers
: Lista de encabezados
headers:
- {header1}: {headerType1}
- {header2}: {headerType2}
helperApi
: contiene de forma recursiva la API auxiliar del panel en el mismo formato que la API principal (opcional, específica para el panel)
validation
: nombre calificado para la función de validación de solicitudes (opcional)
Ejemplo:
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
: Se utiliza para importar tipos predefinidos. Ver más{dataTypeName}
: especifica el nombre del módulo. tableName
: nombre opcional de la tabla. Toma el caso de serpiente del dataTypeName
si no está definido.
fields
: enumera todos los campos de la tabla con tipo Haskell. Ver más
constraints
: clave primaria | Clave secundaria | No nulo | AUTOINCREMENTO
importPackageOverrides
: se utiliza para anular paquetes de importación Ver más
types
: tipos definidos por el usuario, similares a los tipos de API. Ver más
derives
: anula las derivaciones del tipo de datos principal.
derives : " Show,Eq,Ord "
beamType
: tipo de viga definido por el usuario para un tipo de datos específico. Ver más
beamFields
: cambie el nombre del campo de viga definido por el usuario o utilícelo si desea tener algo diferente en el lado de la viga. Ver más
beamInstance
: podemos mencionar la instancia de viga que necesitamos usando este campo Ver más
sqlType
: tipo SQL definido por el usuario para un campo. Ver más
default
: Valor SQL predeterminado para los campos, si los hay.
fields :
scheduleTryTimes : ' [Int] '
tripCategory : Text
default :
tripCategory : " 'All' "
scheduleTryTimes : " '{1800, 900, 300}' "
queries
: todas las consultas de Beam para la tabla. Ver más
cachedQueries:
consultas en caché para la tabla. Ver más
fromTType
: FromTType de campos, si corresponde. Ver más
toTType
: ToTType de campos, si corresponde. Ver más
excludedFields
: hay algunos campos comunes como comercianteId, comercianteOperatingCityId, creado en y actualizado en que se agregan automáticamente en el tipo de datos. Para eliminarlos usa esto.
excludedFields :
- merchantOperatingCityId
- merchantId
extraIndexes
: cualquier índice adicional Ver más
extraOperations
: Operaciones adicionales Ver más
Tienes que proporcionar el nombre del módulo en las importaciones.
imports :
Merchant : Domain.Types.Merchant
FRFSSearch : Domain.Types.FRFSSearch
Nota: Estos tipos comunes se importan automáticamente, por lo que puede usarlos directamente sin importarlos.
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
Para cambiar el paquete:
importPackageOverrides:
Domain.Types.DataType1: dashboard-api
Importación generada en haskell:
import "dashboard-api" Domain.Types.DataType1
A veces es posible que necesitemos omitir la anulación del paquete cuando generamos en el mismo paquete. Luego deberíamos especificar el mapeo de paquetes en las configuraciones dhall
:
, _packageMapping =
[ { _1 = GeneratorType. API_TYPES , _2 = " dashboard-api " }
{ _1 = GeneratorType. SERVANT_API , _2 = " rider-app " }
]
Importación generada en Haskell para API_TYPES
:
import "this" Domain.Types.DataType1
Importación generada en haskell para SERVANT_API
:
import "dashboard-api" Domain.Types.DataType1
En la sección de campo, mencione el nombre del campo y su tipo de Haskell correspondiente.
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
Para los tipos de campo simples (que se acaban de importar), también se copiará en el lado de la viga, a menos que mencionemos un tipo de viga específico.
Si el campo es de tipo complejo, se dividirá recursivamente en el lado de la viga, a menos que mencionemos un tipo de viga específico.
En caso de Id, ShortId el tipo de haz se considerará como texto.
Si queremos el tipo de datos importados en el lado del dominio y la identificación correspondiente en el lado de la viga, usamos las extensiones WithId con la definición de tipo.
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: Esto se usa cuando queremos que el Id. del tipo de datos importado sea el campo Beam. Esto no crea el tipo de datos en la consulta de creación de viga.
WithCachedId: Igual que WithId, la única diferencia es que la consulta create y findbyId se importa desde Storage.CachedQuery.*
WithIdCreate , WithCachedIdCreate: utilice esto cuando sea necesario para crear el tipo de datos en la consulta de creación. Importante: El tipo de datos importado debe tener la función de creación en su archivo de consulta correspondiente. Ejemplo:
fields :
fareParams : Maybe FareParameters|WithIdCreate
farePolicy : Maybe FarePolicy|WithCachedId
Consulta de creación generada:
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
Conversiones ToTType y FromTType generadas:
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
}
Sintaxis:
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)
Donde sintaxis de la cláusula:
where:
- {operator1}:
- field1
- {operator2}:
- field2
- field3
- {operator3}:
- field4
- field5
Los campos que se utilizan en parámetros y cláusula donde pueden ser de 3 tipos:
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
Lista de operadores donde:
Ejemplo:
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
Consulta generada:
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
]