Music Module

Generating the music module

Create module

This creates a new codegen module music-gen, in the libs directory. The gen postfix is added to generated modules, to distinguish them from other modules, coded by hand.

pnx g @logosphere/sdk:module --name music

Take a look at the project structure under libs/music-gen/src

Edit model file

In this step, we will create a simple model for the music domain, consisting of Artist, Album and Track entities, where one artist can have multiple albums and one album can have multiple tracks. We also have a predefined enumeration for genres.

The *.model.ts file that is pre-generated with a few sample entities, should be the only modifiable file in a codegen library. The rest of the code assets will be generated from the model defined in this file.

The model is based on TypeScript classes and is very similar to code-first modelling in other TypeScript frameworks, such as NestJS, TypeORM etc. Every entity, as per DDD definition of an entity is defined as a class with an @Ent() decorator and every property within an entity should be decorated with a @Prop() decorator. Enumerations should be registered with registerEnum() function.

For the complete list of model properties check the Logosphere Modelingarrow-up-right documentation.

Modify libs/music-gen/src/music.model.ts file to add the following entities. Delete generated MusicEntity. Leave Wallet, WalletAsset, and User entities unmodified.

/* eslint-disable @typescript-eslint/no-unused-vars */
import { Ent, Prop, registerEnum } from '@logosphere/sdk';

export enum Genre
{
    Pop = 0,
    Rock,
    HipHop,
    RnB,
    Country,
    KPop,
    JPop,
    World
};

registerEnum(Genre, 'Genre');

@Ent('artist')
export class Artist {
  @Prop({
    examples: ['Taylor Swift', 'Lil Nas X', 'Dua Lipa'],
  })
  name: string;

  @Prop({
    examples: ['About Taylor Swift', 'Lil Nas X Bio', 'Dua Lipa is awesome'],
  })
  about: string;

}

@Ent('album')
export class Album {
  @Prop({
    examples: ['Fearless', 'MONTERO', 'Future Nostalgia'],
  })
  name: string;

  @Prop({ type: () => Genre })
  genre: Genre;

  @Prop({ type: () => Artist, index: false })
  artist: Artist;

}

@Ent('track')
export class Track {
  @Prop({
    examples: ['Shake If Off', 'Montero', 'Levitating'],
  })
  name: string;

  @Prop({ type: () => Genre })
  genre: Genre;

  @Prop({
    examples: ['Be yourself and shake it off', 'Also called call me by your name', 'Just a great song'],
  })
  description: string;

  @Prop({ type: () => Album, index: false })
  album: Album;

  @Prop({ type: () => Artist, index: false })
  artist: Artist;

}

@Ent('playlist')
export class Playlist {
  @Prop({
    examples: ['Top 40', 'Best of Pop', 'New releases'],
  })
  name: string;

  @Prop({ type: () => [Track], index: false})
  tracks: Track[];

}

@Ent('walletAsset')
class WalletAsset {
  @Prop({ doc: 'Name of the asset', examples: ['4269736f6e'] })
  name: string;

  @Prop({
    doc: 'Fingerprint of the asset',
    examples: ['asset12q7zh30hj2yme96wy8ms4fcdrwtep0auz8xqly'],
  })
  fingerprint: string;

  @Prop({
    doc: 'Policy ID of the asset',
    examples: ['0b7018936bc41808ddabd96b4908b583195a0c252b5752ad38012bdb'],
  })
  policyId: string;

  @Prop({ doc: 'Quantity of the asset', examples: ['1'] })
  quantity: number;

  @Prop({ doc: 'Metadata associated with the asset' })
  metadata: string;

  @Prop({ doc: 'Fluree subject ID of the asset', examples: ['87960930223082'] })
  assetSubjectId: string;

  @Prop({
    doc: 'Logosphere ID of the asset',
    examples: [
      '62c0ac76d6eebbf70828da57ea06c41a55001a2eb3cc929206d8f39abdbfaefc',
    ],
  })
  logosphereId: string;
}

@Ent('wallet')
class Wallet {
  @Prop({
    doc: 'Name of the wallet',
    examples: ['Babingos wallet'],
  })
  name: string;

  @Prop({
    doc: 'ID of the wallet',
    index: true,
    unique: true,
    examples: ['cd72843c95467883ccd6dafe227b91c96f071713'],
  })
  walletId: string;

  @Prop({
    doc: 'Address of the wallet',
    index: true,
    unique: true,
    examples: [
      'addr_test1qzsn8km55mp6cra7l20cymauks2fay8sqc3jr874mgmxpsa5mj4dvw5zrxmhauknj60c8tsf7x72ng0r8zmxa3necjlsgx9q6d',
    ],
  })
  address: string;

  @Prop({
    doc: 'Public key of the wallet',
    index: true,
    unique: true,
    examples: [
      'a1f009e6f5770c7b10729f27237c7ccc677739e31119a69766664dee611220948234926b2c445c2e9e2ff40f22beafa193d7fedf72e5e877bffd606d33b6638c',
    ],
  })
  publicKey: string;
  
  @Prop({
    doc: 'Balance of the wallet in lovelace',
    examples: [
      '0', '1000'
    ],
  })
  balance: number;

  @Prop({
    doc: 'Wallet assets',
    type: () => [WalletAsset],
  })
  assets: WalletAsset[];
}

@Ent('user')
class User {
  @Prop({
    doc: 'Username of the user',
    index: true,
    unique: true,
    examples: ['babingo_whoelse'],
  })
  username: string;

  @Prop({
    doc: 'User wallet',
    type: () => Wallet,
  })
  wallet: Wallet;
}

Generate API

Build model with the following command:

NX automatically tracks changes and only calls build target for affected libs. Alternatively you can build the codegen module with:

Now you can generate GraphQL API with DDD assets architecture:

After executing this command, you will see your music codegen module library populated with DDD assets and the two applications generated under apps folder:

  • apps/music-e2e

  • apps/music

The music-e2e is an app for running integration tests against it.

The music-app is an application that is built into a docker container and serves the GQL API

Create environment variables and docker-compose

To run, the application require set of environment variables and number of external services. To generate pre-configured .env and docker-compose files call the command below

circle-info

If you already have one of generated files, the generator will ask you about override. In case of confirm the previous configuration will be lost

Shell environment variables

These environment variables will be used with the Logosphere NX plugin generators pnx g command and should be defined in a shell config file, such as .zshrc or in a file, such as .env-sh which should be sourced with source .env-sh

Run single Docker image

Modify tags option in the docker target in the apps/music/project.json from logosphere to your DockerHub organization:

Build docker image:

Run whole application stack in Docker

Make sure that docker-compose and .env exists and contain proper values. Docker service must be running too. Being in root folder execute command:

Test API

Navigate to http://localhost:3000/graphqlarrow-up-right and Apollo Sandbox will open for testing GraphQL queries and mutations

Get All Tracks

Copy & paste the trackFindAll query into the Apollo Sandbox

Expectedly it should return empty result, since there is no data in the ledger yet.

Create a track

Copy & paste the trackSave mutation and variables into the Apollo Sandbox

Variables:

Output:

Last updated