Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI command added for "lb4 cache --config" #8420

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions docs/site/Cache-generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
lang: en
title: 'Cache generator'
keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, CLI
sidebar: lb4_sidebar
permalink: /doc/en/lb4/Cache-generator.html
---

{% include content/generator-create-app.html lang=page.lang %}

### Synopsis

Add cache to LoopBack4 application using Redis datasource.

```sh
lb4 [cache] [options]
```

### Options

`--datasource` : Valid Redis datasource name.

{% include_relative includes/CLI-std-options.md %}

### Interactive Prompts

The tool will prompt you for:

- **Name of the datasource.** If the datasource had been supplied from the
command line, the prompt is skipped and the datasource from command-line
argument is used.

### Output

Following files are generated as result of this command:

```text
.
├── src/
| ├── models/
| | └── cache.model.ts
| ├── providers/
| | └── cache-strategy.provider.ts
| ├── repositories/
| | └── cache.repository.ts
```

`cache.model.ts` is a model that cache repository uses to read and write data
into datasource.

`cache-strategy.provider.ts` provides the implementation of caching logic. You
can modify this file to extend caching capabilities.

`cache.repository.ts` is a repository that connects to Redis datasource and
reads and writes values from datasource.

The command also modifies `applications.ts` to include `CacheComponent`.

```
this.component(CacheComponent);
this.bind(CacheBindings.CACHE_STRATEGY).toProvider(CacheStrategyProvider);
```
4 changes: 4 additions & 0 deletions docs/site/sidebars/lb4_sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@ children:
url: Copyright-generator.html
output: 'web, pdf'

- title: 'Cache generator'
url: Cache-generator.html
output: 'web, pdf'

- title: 'Connectors reference'
url: Connectors-reference.html
output: 'web'
Expand Down
71 changes: 71 additions & 0 deletions packages/cli/.yo-rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,77 @@
},
"arguments": [],
"name": "copyright"
},
"cache": {
"options": {
"help": {
"name": "help",
"type": "Boolean",
"alias": "h",
"description": "Print the generator's options and usage"
},
"skip-cache": {
"name": "skip-cache",
"type": "Boolean",
"description": "Do not remember prompt answers",
"default": false
},
"skip-install": {
"name": "skip-install",
"type": "Boolean",
"description": "Do not automatically install dependencies",
"default": false
},
"force-install": {
"name": "force-install",
"type": "Boolean",
"description": "Fail on install dependencies error",
"default": false
},
"ask-answered": {
"type": "Boolean",
"description": "Show prompts for already configured options",
"default": false,
"name": "ask-answered",
"hide": false
},
"datasource": {
"type": "String",
"required": false,
"description": "A valid redis datasource",
"name": "datasource",
"hide": false
},
"config": {
"type": "String",
"alias": "c",
"description": "JSON file name or value to configure options",
"name": "config",
"hide": false
},
"yes": {
"type": "Boolean",
"alias": "y",
"description": "Skip all confirmation prompts with default or provided value",
"name": "yes",
"hide": false
},
"format": {
"type": "Boolean",
"description": "Format generated code using npm run lint:fix",
"name": "format",
"hide": false
},
"packageManager": {
"type": "String",
"description": "Change the default package manager",
"alias": "pm",
"name": "packageManager",
"hide": false
}
},
"arguments": [],
"name": "cache"
}
}
}
78 changes: 78 additions & 0 deletions packages/cli/generators/cache/cache-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict';
const ast = require('ts-morph');
const path = require('path');

/**
* Update `src/application.ts`
* @param projectRoot - Project root directory
*/
async function updateApplicationTs(projectRoot) {
const CACHE_COMPONENT = 'CacheComponent';
const CACHE_BINDINGS = 'CacheBindings';
const PROVIDER = 'CacheStrategyProvider';
const applicationTsFile = path.join(projectRoot, 'src/application.ts');

// Create an AST project
const project = new ast.Project({
manipulationSettings: {
indentationText: ast.IndentationText.TwoSpaces,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: false,
newLineKind: ast.NewLineKind.LineFeed,
quoteKind: ast.QuoteKind.Single,
},
});

// import {CacheBindings, CacheComponent} from 'loopback-api-cache';
const cacheImportDeclaration = {
kind: ast.StructureKind.ImportDeclaration,
moduleSpecifier: 'loopback-api-cache',
namedImports: [CACHE_BINDINGS, CACHE_COMPONENT],
};

const pFile = project.addSourceFileAtPath(applicationTsFile);

// Check if `import {CacheBindings, CacheComponent} from 'loopback-api-cache'` exists
const cacheImport = pFile.getImportDeclaration('loopback-api-cache');

if (cacheImport == null) {
// Not found
pFile.addImportDeclaration(cacheImportDeclaration);
} else {
// Further check named import for CacheComponent
const names = cacheImport.getNamedImports().map(i => i.getName());
if (!names.includes(CACHE_COMPONENT)) {
cacheImport.addNamedImport(CACHE_COMPONENT);
}
if (!names.includes(CACHE_BINDINGS)) {
cacheImport.addNamedImport(CACHE_BINDINGS);
}
}

// import {CacheStrategyProvider} from './providers';
const providerImportDeclaration = {
kind: ast.StructureKind.ImportDeclaration,
moduleSpecifier: './providers',
namedImports: [PROVIDER],
};
const providerImport = pFile.getImportDeclaration('./providers');
if (providerImport == null) {
// Not found
pFile.addImportDeclaration(providerImportDeclaration);
}

// Find the constructor
const ctor = pFile.getClasses()[0].getConstructors()[0];
const body = ctor.getBodyText();

// Check if `this.component(CacheComponent)` exists
if (!body.includes(`this.component(${CACHE_COMPONENT}`)) {
ctor.addStatements(`this.component(${CACHE_COMPONENT});`);
ctor.addStatements(
`this.bind(${CACHE_BINDINGS}.CACHE_STRATEGY).toProvider(${PROVIDER});`,
);
}

await pFile.save();
}

exports.updateApplicationTs = updateApplicationTs;
Loading