diff --git a/npm/ng-packs/packages/schematics/src/collection.json b/npm/ng-packs/packages/schematics/src/collection.json index 9b21d174cab..5c2df8ea366 100644 --- a/npm/ng-packs/packages/schematics/src/collection.json +++ b/npm/ng-packs/packages/schematics/src/collection.json @@ -1,9 +1,19 @@ { "schematics": { - "proxy": { - "description": "ABP Proxy Generator Schematics", - "factory": "./commands/proxy", - "schema": "./commands/proxy/schema.json" + "proxy-add": { + "description": "ABP Proxy Generator Add Schematics", + "factory": "./commands/proxy-add", + "schema": "./commands/proxy-add/schema.json" + }, + "proxy-refresh": { + "description": "ABP Proxy Generator Refresh Schematics", + "factory": "./commands/proxy-refresh", + "schema": "./commands/proxy-refresh/schema.json" + }, + "proxy-remove": { + "description": "ABP Proxy Generator Remove Schematics", + "factory": "./commands/proxy-remove", + "schema": "./commands/proxy-remove/schema.json" }, "api": { "description": "ABP API Generator Schematics", diff --git a/npm/ng-packs/packages/schematics/src/commands/api/files-enum/shared/enums/__namespace@dir__/__name@kebab__.ts.template b/npm/ng-packs/packages/schematics/src/commands/api/files-enum/proxy/enums/__namespace@dir__/__name@kebab__.ts.template similarity index 100% rename from npm/ng-packs/packages/schematics/src/commands/api/files-enum/shared/enums/__namespace@dir__/__name@kebab__.ts.template rename to npm/ng-packs/packages/schematics/src/commands/api/files-enum/proxy/enums/__namespace@dir__/__name@kebab__.ts.template diff --git a/npm/ng-packs/packages/schematics/src/commands/api/files-model/shared/models/__namespace@dir__/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/api/files-model/proxy/models/__namespace@dir__/index.ts.template similarity index 100% rename from npm/ng-packs/packages/schematics/src/commands/api/files-model/shared/models/__namespace@dir__/index.ts.template rename to npm/ng-packs/packages/schematics/src/commands/api/files-model/proxy/models/__namespace@dir__/index.ts.template diff --git a/npm/ng-packs/packages/schematics/src/commands/api/files-service/shared/services/__namespace@dir__/__name@kebab__.service.ts.template b/npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/services/__namespace@dir__/__name@kebab__.service.ts.template similarity index 100% rename from npm/ng-packs/packages/schematics/src/commands/api/files-service/shared/services/__namespace@dir__/__name@kebab__.service.ts.template rename to npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/services/__namespace@dir__/__name@kebab__.service.ts.template diff --git a/npm/ng-packs/packages/schematics/src/commands/api/index.ts b/npm/ng-packs/packages/schematics/src/commands/api/index.ts index 24e0f816afc..646f2170eaf 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/api/index.ts @@ -10,15 +10,17 @@ import { url, } from '@angular-devkit/schematics'; import { Exception } from '../../enums'; -import { ServiceGeneratorParams } from '../../models'; +import { GenerateProxySchema, ServiceGeneratorParams } from '../../models'; import { applyWithOverwrite, buildDefaultPath, - createApiDefinitionReader, createControllerToServiceMapper, createImportRefsToModelReducer, createImportRefToEnumMapper, + createProxyConfigReader, + createProxyConfigWriterCreator, EnumGeneratorParams, + generateProxyConfigJson, getEnumNamesFromImports, getRootNamespace, interpolate, @@ -28,7 +30,6 @@ import { serializeParameters, } from '../../utils'; import * as cases from '../../utils/text'; -import { Schema as GenerateProxySchema } from './schema'; export default function(schema: GenerateProxySchema) { const params = removeDefaultPlaceholders(schema); @@ -40,9 +41,9 @@ export default function(schema: GenerateProxySchema) { const target = await resolveProject(tree, params.target!); const solution = getRootNamespace(tree, source, moduleName); const targetPath = buildDefaultPath(target.definition); - const definitionPath = `${targetPath}/shared/api-definition.json`; - const readApiDefinition = createApiDefinitionReader(definitionPath); - const data = readApiDefinition(tree); + const readProxyConfig = createProxyConfigReader(targetPath); + const createProxyConfigWriter = createProxyConfigWriterCreator(targetPath); + const data = readProxyConfig(tree); const types = data.types; const modules = data.modules; if (!types || !modules) throw new SchematicsException(Exception.InvalidApiDefinition); @@ -80,7 +81,14 @@ export default function(schema: GenerateProxySchema) { modelImports, }); - return branchAndMerge(chain([generateServices, generateModels, generateEnums])); + if (!data.generated.includes(moduleName)) data.generated.push(moduleName); + data.generated.sort(); + const json = generateProxyConfigJson(data); + const overwriteProxyConfig = createProxyConfigWriter('overwrite', json); + + return branchAndMerge( + chain([generateServices, generateModels, generateEnums, overwriteProxyConfig]), + ); }, ]); } diff --git a/npm/ng-packs/packages/schematics/src/commands/api/schema.ts b/npm/ng-packs/packages/schematics/src/commands/api/schema.ts deleted file mode 100644 index 0c26ce461f8..00000000000 --- a/npm/ng-packs/packages/schematics/src/commands/api/schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Schema { - /** - * Backend module to generate code for - */ - module?: string; - - /** - * Angular project to resolve root namespace & API definition URL from - */ - source?: string; - - /** - * Angular project to generate code in - */ - target?: string; -} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts new file mode 100644 index 00000000000..1400cfca1c6 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts @@ -0,0 +1,51 @@ +import { strings } from '@angular-devkit/core'; +import { chain, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { GenerateProxySchema } from '../../models'; +import { + buildDefaultPath, + chainAndMerge, + createApiDefinitionGetter, + createApisGenerator, + createProxyClearer, + createProxyConfigReader, + createProxyConfigSaver, + createProxyWarningSaver, + removeDefaultPlaceholders, + resolveProject, +} from '../../utils'; + +export default function(schema: GenerateProxySchema) { + const params = removeDefaultPlaceholders(schema); + const moduleName = strings.camelize(params.module || 'app'); + + return chain([ + async (host: Tree, _context: SchematicContext) => { + const target = await resolveProject(host, params.target!); + const targetPath = buildDefaultPath(target.definition); + const readProxyConfig = createProxyConfigReader(targetPath); + let generated: string[] = []; + + try { + generated = readProxyConfig(host).generated; + const index = generated.findIndex(m => m === moduleName); + if (index < 0) generated.push(moduleName); + } catch (_) { + generated.push(moduleName); + } + + const getApiDefinition = createApiDefinitionGetter(params); + const data = { generated, ...(await getApiDefinition(host)) }; + data.generated = []; + + const clearProxy = createProxyClearer(targetPath); + + const saveProxyConfig = createProxyConfigSaver(data, targetPath); + + const saveProxyWarning = createProxyWarningSaver(targetPath); + + const generateApis = createApisGenerator(schema, generated); + + return chainAndMerge([clearProxy, saveProxyConfig, saveProxyWarning, generateApis])(host); + }, + ]); +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json similarity index 50% rename from npm/ng-packs/packages/schematics/src/commands/proxy/schema.json rename to npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json index f0b76bbdf63..f9e762c597b 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json @@ -6,33 +6,33 @@ "properties": { "module": { "alias": "m", - "description": "Backend module to generate code for", + "description": "Backend module name", "type": "string", "$default": { "$source": "argv", "index": 0 }, - "x-prompt": "Please enter name of the backend module you wish to generate proxies for. (default: \"app\")" + "x-prompt": "Please enter backend module name. (default: \"app\")" }, "source": { "alias": "s", - "description": "Angular project to resolve root namespace & API definition URL from", + "description": "Source Angular project for API definition URL & root namespace resolution", "type": "string", "$default": { "$source": "argv", "index": 1 }, - "x-prompt": "Plese enter Angular project name to resolve root namespace & API definition URL from. (default: workspace \"defaultProject\")" + "x-prompt": "Plese enter source Angular project for API definition URL & root namespace resolution. (default: workspace \"defaultProject\")" }, "target": { "alias": "t", - "description": "Angular project to generate code in", + "description": "Target Angular project to place the generated code", "type": "string", "$default": { "$source": "argv", "index": 2 }, - "x-prompt": "Plese enter Angular project name to place generated code in. (default: workspace \"defaultProject\")" + "x-prompt": "Plese enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" } }, "required": [] diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts new file mode 100644 index 00000000000..2c4e20d7d22 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts @@ -0,0 +1,37 @@ +import { SchematicContext, Tree } from '@angular-devkit/schematics'; +import { GenerateProxySchema } from '../../models'; +import { + buildDefaultPath, + chainAndMerge, + createApiDefinitionGetter, + createApisGenerator, + createProxyClearer, + createProxyConfigReader, + createProxyConfigSaver, + removeDefaultPlaceholders, + resolveProject, +} from '../../utils'; + +export default function(schema: GenerateProxySchema) { + const params = removeDefaultPlaceholders(schema); + + return async (host: Tree, _context: SchematicContext) => { + const target = await resolveProject(host, params.target!); + const targetPath = buildDefaultPath(target.definition); + + const readProxyConfig = createProxyConfigReader(targetPath); + const { generated } = readProxyConfig(host); + + const getApiDefinition = createApiDefinitionGetter(params); + const data = { generated, ...(await getApiDefinition(host)) }; + data.generated = []; + + const clearProxy = createProxyClearer(targetPath); + + const saveProxyConfig = createProxyConfigSaver(data, targetPath); + + const generateApis = createApisGenerator(schema, generated); + + return chainAndMerge([clearProxy, saveProxyConfig, generateApis])(host); + }; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json new file mode 100644 index 00000000000..f9e762c597b --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsAbpGenerateProxy", + "title": "ABP Generate Proxy Schema", + "type": "object", + "properties": { + "module": { + "alias": "m", + "description": "Backend module name", + "type": "string", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "Please enter backend module name. (default: \"app\")" + }, + "source": { + "alias": "s", + "description": "Source Angular project for API definition URL & root namespace resolution", + "type": "string", + "$default": { + "$source": "argv", + "index": 1 + }, + "x-prompt": "Plese enter source Angular project for API definition URL & root namespace resolution. (default: workspace \"defaultProject\")" + }, + "target": { + "alias": "t", + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 2 + }, + "x-prompt": "Plese enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" + } + }, + "required": [] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts new file mode 100644 index 00000000000..83cc43c0b83 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts @@ -0,0 +1,43 @@ +import { strings } from '@angular-devkit/core'; +import { SchematicContext, Tree } from '@angular-devkit/schematics'; +import { GenerateProxySchema } from '../../models'; +import { + buildDefaultPath, + chainAndMerge, + createApiDefinitionGetter, + createApisGenerator, + createProxyClearer, + createProxyConfigReader, + createProxyConfigSaver, + removeDefaultPlaceholders, + resolveProject, +} from '../../utils'; + +export default function(schema: GenerateProxySchema) { + const params = removeDefaultPlaceholders(schema); + const moduleName = strings.camelize(params.module || 'app'); + + return async (host: Tree, _context: SchematicContext) => { + const target = await resolveProject(host, params.target!); + const targetPath = buildDefaultPath(target.definition); + + const readProxyConfig = createProxyConfigReader(targetPath); + const { generated } = readProxyConfig(host); + + const index = generated.findIndex(m => m === moduleName); + if (index < 0) return host; + generated.splice(index, 1); + + const getApiDefinition = createApiDefinitionGetter(params); + const data = { generated, ...(await getApiDefinition(host)) }; + data.generated = []; + + const clearProxy = createProxyClearer(targetPath); + + const saveProxyConfig = createProxyConfigSaver(data, targetPath); + + const generateApis = createApisGenerator(schema, generated); + + return chainAndMerge([clearProxy, saveProxyConfig, generateApis])(host); + }; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json new file mode 100644 index 00000000000..f9e762c597b --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsAbpGenerateProxy", + "title": "ABP Generate Proxy Schema", + "type": "object", + "properties": { + "module": { + "alias": "m", + "description": "Backend module name", + "type": "string", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "Please enter backend module name. (default: \"app\")" + }, + "source": { + "alias": "s", + "description": "Source Angular project for API definition URL & root namespace resolution", + "type": "string", + "$default": { + "$source": "argv", + "index": 1 + }, + "x-prompt": "Plese enter source Angular project for API definition URL & root namespace resolution. (default: workspace \"defaultProject\")" + }, + "target": { + "alias": "t", + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 2 + }, + "x-prompt": "Plese enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" + } + }, + "required": [] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts deleted file mode 100644 index ddbbd68caa1..00000000000 --- a/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { strings } from '@angular-devkit/core'; -import { - branchAndMerge, - chain, - schematic, - SchematicContext, - Tree, -} from '@angular-devkit/schematics'; -import { API_DEFINITION_ENDPOINT } from '../../constants'; -import { ApiDefinition } from '../../models'; -import { - buildDefaultPath, - createApiDefinitionSaver, - getApiDefinition, - getSourceUrl, - removeDefaultPlaceholders, - resolveProject, -} from '../../utils'; -import { Schema as GenerateProxySchema } from './schema'; - -export default function(schema: GenerateProxySchema) { - const params = removeDefaultPlaceholders(schema); - const moduleName = strings.camelize(params.module || 'app'); - - return chain([ - async (tree: Tree, _context: SchematicContext) => { - const source = await resolveProject(tree, params.source!); - const target = await resolveProject(tree, params.target!); - const sourceUrl = getSourceUrl(tree, source, moduleName); - const targetPath = buildDefaultPath(target.definition); - const data: ApiDefinition = await getApiDefinition(sourceUrl + API_DEFINITION_ENDPOINT); - - const saveApiDefinition = createApiDefinitionSaver( - data, - `${targetPath}/shared/api-definition.json`, - ); - const createApi = schematic('api', schema); - - return branchAndMerge(chain([saveApiDefinition, createApi])); - }, - ]); -} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy/schema.ts b/npm/ng-packs/packages/schematics/src/commands/proxy/schema.ts deleted file mode 100644 index 0c26ce461f8..00000000000 --- a/npm/ng-packs/packages/schematics/src/commands/proxy/schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Schema { - /** - * Backend module to generate code for - */ - module?: string; - - /** - * Angular project to resolve root namespace & API definition URL from - */ - source?: string; - - /** - * Angular project to generate code in - */ - target?: string; -} diff --git a/npm/ng-packs/packages/schematics/src/constants/index.ts b/npm/ng-packs/packages/schematics/src/constants/index.ts index 01053d93337..cd95fb52010 100644 --- a/npm/ng-packs/packages/schematics/src/constants/index.ts +++ b/npm/ng-packs/packages/schematics/src/constants/index.ts @@ -1,3 +1,4 @@ export * from './api'; +export * from './proxy'; export * from './system-types'; export * from './volo'; diff --git a/npm/ng-packs/packages/schematics/src/constants/proxy.ts b/npm/ng-packs/packages/schematics/src/constants/proxy.ts new file mode 100644 index 00000000000..fbf7f127bd5 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/constants/proxy.ts @@ -0,0 +1,18 @@ +export const PROXY_PATH = '/proxy'; +export const PROXY_CONFIG_PATH = `${PROXY_PATH}/generate-proxy.json`; +export const PROXY_WARNING_PATH = `${PROXY_PATH}/README.md`; + +export const PROXY_WARNING = `# Proxy Generation Output + +This directory includes the output of the latest proxy generation. +The \`services\`, \`models\`, and \`enums\` folders will be overwritten when proxy generation is run again. +Therefore, please do not place your own content in those folders. + +In addition, \`generate-proxy.json\` works like a lock file. +It includes information used by the proxy generator, so please do not delete or modify it. + +Finally, the name of this folder should not be changed for two reasons: +- Proxy generator will keep creating this folder and you will have multiple copies of the same content. +- ABP Suite generates imports from this folder and uses the path \`/proxy\` when doing so. + +`; diff --git a/npm/ng-packs/packages/schematics/src/enums/exception.ts b/npm/ng-packs/packages/schematics/src/enums/exception.ts index 6c2e635edfc..98c92493d61 100644 --- a/npm/ng-packs/packages/schematics/src/enums/exception.ts +++ b/npm/ng-packs/packages/schematics/src/enums/exception.ts @@ -1,11 +1,13 @@ export const enum Exception { + DirRemoveFailed = '[Directory Remove Failed] Cannot remove "{0}".', FileNotFound = '[File Not Found] There is no file at "{0}" path.', + FileWriteFailed = '[File Write Failed] Cannot write file at "{0}".', InvalidModule = '[Invalid Module] Backend module "{0}" does not exist in API definition.', InvalidApiDefinition = '[Invalid API Definition] The provided API definition is invalid.', InvalidWorkspace = '[Invalid Workspace] The angular.json should be a valid JSON file.', NoApi = '[API Not Available] Please double-check the URL in the source project environment and make sure your application is up and running.', - NoApiDefinition = '[API Definition Not Found] There is no API definition file at "{0}".', NoProject = '[Project Not Found] Either define a default project in your workspace or specify the project name in schematics options.', + NoProxyConfig = '[Proxy Config Not Found] There is no JSON file at "{0}".', NoTypeDefinition = '[Type Definition Not Found] There is no type definition for "{0}".', NoWorkspace = '[Workspace Not Found] Make sure you are running schematics at the root directory of your workspace and it has an angular.json file.', NoEnvironment = '[Environment Not Found] An environment file cannot be located in "{0}" project.', diff --git a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts new file mode 100644 index 00000000000..59c4e6adabf --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts @@ -0,0 +1,16 @@ +export interface GenerateProxySchema { + /** + * Backend module name + */ + module?: string; + + /** + * Source Angular project for API definition URL & root namespace resolution + */ + source?: string; + + /** + * Target Angular project to place the generated code + */ + target?: string; +} diff --git a/npm/ng-packs/packages/schematics/src/models/index.ts b/npm/ng-packs/packages/schematics/src/models/index.ts index 900b58b4f12..176c9c66177 100644 --- a/npm/ng-packs/packages/schematics/src/models/index.ts +++ b/npm/ng-packs/packages/schematics/src/models/index.ts @@ -1,7 +1,10 @@ export * from './api-definition'; +export * from './generate-proxy-schema'; export * from './import'; export * from './method'; export * from './model'; export * from './project'; +export * from './proxy-config'; export * from './service'; +export * from './tree'; export * from './util'; diff --git a/npm/ng-packs/packages/schematics/src/models/proxy-config.ts b/npm/ng-packs/packages/schematics/src/models/proxy-config.ts new file mode 100644 index 00000000000..226833da124 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/models/proxy-config.ts @@ -0,0 +1,5 @@ +import { ApiDefinition } from './api-definition'; + +export interface ProxyConfig extends ApiDefinition { + generated: string[]; +} diff --git a/npm/ng-packs/packages/schematics/src/models/tree.ts b/npm/ng-packs/packages/schematics/src/models/tree.ts new file mode 100644 index 00000000000..3c17cfcb102 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/models/tree.ts @@ -0,0 +1 @@ +export type WriteOp = 'create' | 'overwrite'; diff --git a/npm/ng-packs/packages/schematics/src/utils/api.ts b/npm/ng-packs/packages/schematics/src/utils/api.ts new file mode 100644 index 00000000000..f772666b4b8 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/api.ts @@ -0,0 +1,11 @@ +import { chain, schematic } from '@angular-devkit/schematics'; +import { GenerateProxySchema } from '../models'; + +export function createApisGenerator(schema: GenerateProxySchema, generated: string[]) { + return chain( + generated.map(m => { + const apiSchema = { ...schema, source: schema.target, module: m }; + return schematic('api', apiSchema); + }), + ); +} diff --git a/npm/ng-packs/packages/schematics/src/utils/index.ts b/npm/ng-packs/packages/schematics/src/utils/index.ts index badfaa79ee5..304c2294b08 100644 --- a/npm/ng-packs/packages/schematics/src/utils/index.ts +++ b/npm/ng-packs/packages/schematics/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './angular'; +export * from './api'; export * from './ast'; export * from './common'; export * from './enum'; diff --git a/npm/ng-packs/packages/schematics/src/utils/rule.ts b/npm/ng-packs/packages/schematics/src/utils/rule.ts index 6748f30fab2..39b14cec787 100644 --- a/npm/ng-packs/packages/schematics/src/utils/rule.ts +++ b/npm/ng-packs/packages/schematics/src/utils/rule.ts @@ -1,6 +1,8 @@ import { apply, + chain, forEach, + MergeStrategy, mergeWith, Rule, SchematicContext, @@ -16,6 +18,14 @@ export function applyWithOverwrite(source: Source, rules: Rule[]): Rule { }; } +export function chainAndMerge(rules: Rule[]) { + return (host: Tree) => async (tree: Tree, context: SchematicContext) => + host.merge( + (await (chain(rules)(tree, context) as any).toPromise()) as Tree, + MergeStrategy.AllowDeleteConflict, + ); +} + export function overwriteFileIfExists(tree: Tree): Rule { return forEach(fileEntry => { if (!tree.exists(fileEntry.path)) return fileEntry; diff --git a/npm/ng-packs/packages/schematics/src/utils/source.ts b/npm/ng-packs/packages/schematics/src/utils/source.ts index 8a41776955b..b495e75c200 100644 --- a/npm/ng-packs/packages/schematics/src/utils/source.ts +++ b/npm/ng-packs/packages/schematics/src/utils/source.ts @@ -1,13 +1,32 @@ +import { strings } from '@angular-devkit/core'; import { SchematicsException, Tree } from '@angular-devkit/schematics'; import got from 'got'; +import { + API_DEFINITION_ENDPOINT, + PROXY_CONFIG_PATH, + PROXY_PATH, + PROXY_WARNING, + PROXY_WARNING_PATH, +} from '../constants'; import { Exception } from '../enums'; -import { ApiDefinition, Project } from '../models'; +import { ApiDefinition, GenerateProxySchema, Project, ProxyConfig, WriteOp } from '../models'; import { getAssignedPropertyFromObjectliteral } from './ast'; import { interpolate } from './common'; -import { readEnvironment } from './workspace'; +import { readEnvironment, resolveProject } from './workspace'; -export async function getApiDefinition(url: string) { - let body: any; +export function createApiDefinitionGetter(params: GenerateProxySchema) { + const moduleName = strings.camelize(params.module || 'app'); + + return async (host: Tree) => { + const source = await resolveProject(host, params.source!); + const sourceUrl = getSourceUrl(host, source, moduleName); + return await getApiDefinition(sourceUrl); + }; +} + +async function getApiDefinition(sourceUrl: string) { + const url = sourceUrl + API_DEFINITION_ENDPOINT; + let body: ApiDefinition; try { ({ body } = await got(url, { @@ -17,9 +36,10 @@ export async function getApiDefinition(url: string) { })); } catch ({ response }) { // handle redirects - if (response?.body && response.statusCode < 400) return response.body; + if (response.statusCode >= 400 || !response?.body) + throw new SchematicsException(Exception.NoApi); - throw new SchematicsException(Exception.NoApi); + body = response.body; } return body; @@ -71,23 +91,97 @@ export function getSourceUrl(tree: Tree, project: Project, moduleName: string) { return assignment.replace(/[`'"]/g, ''); } -export function createApiDefinitionReader(targetPath: string) { +export function createProxyConfigReader(targetPath: string) { + targetPath += PROXY_CONFIG_PATH; + return (tree: Tree) => { try { const buffer = tree.read(targetPath); - const apiDefinition: ApiDefinition = JSON.parse(buffer!.toString()); - return apiDefinition; + return JSON.parse(buffer!.toString()) as ProxyConfig; } catch (_) {} - throw new SchematicsException(interpolate(Exception.NoApiDefinition, targetPath)); + throw new SchematicsException(interpolate(Exception.NoProxyConfig, targetPath)); }; } -export function createApiDefinitionSaver(apiDefinition: ApiDefinition, targetPath: string) { +export function createProxyClearer(targetPath: string) { + targetPath += PROXY_PATH; + return (tree: Tree) => { - tree[tree.exists(targetPath) ? 'overwrite' : 'create']( - targetPath, - JSON.stringify(apiDefinition, null, 2), - ); + try { + tree.getDir(targetPath).subdirs.forEach(dirName => { + if (!['enums', 'models', 'services'].includes(dirName)) return; + + tree.delete(`${targetPath}/${dirName}`); + }); + + return tree; + } catch (_) { + throw new SchematicsException(interpolate(Exception.DirRemoveFailed, targetPath)); + } }; } + +export function createProxyWarningSaver(targetPath: string) { + targetPath += PROXY_WARNING_PATH; + const createFileWriter = createFileWriterCreator(targetPath); + + return (tree: Tree) => { + const op = tree.exists(targetPath) ? 'overwrite' : 'create'; + const writeWarningMD = createFileWriter(op, PROXY_WARNING); + writeWarningMD(tree); + + return tree; + }; +} + +export function createProxyConfigSaver(apiDefinition: ApiDefinition, targetPath: string) { + const createProxyConfigJson = createProxyConfigJsonCreator(apiDefinition); + const readPreviousConfig = createProxyConfigReader(targetPath); + const createProxyConfigWriter = createProxyConfigWriterCreator(targetPath); + targetPath += PROXY_CONFIG_PATH; + + return (tree: Tree) => { + const generated: string[] = []; + let op: WriteOp = 'create'; + + if (tree.exists(targetPath)) { + op = 'overwrite'; + + try { + readPreviousConfig(tree).generated.forEach(m => generated.push(m)); + } catch (_) {} + } + + const json = createProxyConfigJson(generated); + const writeProxyConfig = createProxyConfigWriter(op, json); + writeProxyConfig(tree); + + return tree; + }; +} + +export function createProxyConfigWriterCreator(targetPath: string) { + targetPath += PROXY_CONFIG_PATH; + + return createFileWriterCreator(targetPath); +} + +export function createFileWriterCreator(targetPath: string) { + return (op: WriteOp, data: string) => (tree: Tree) => { + try { + tree[op](targetPath, data); + return tree; + } catch (_) {} + + throw new SchematicsException(interpolate(Exception.FileWriteFailed, targetPath)); + }; +} + +export function createProxyConfigJsonCreator(apiDefinition: ApiDefinition) { + return (generated: string[]) => generateProxyConfigJson({ generated, ...apiDefinition }); +} + +export function generateProxyConfigJson(proxyConfig: ProxyConfig) { + return JSON.stringify(proxyConfig, null, 2); +} diff --git a/npm/ng-packs/scripts/build-schematics.ts b/npm/ng-packs/scripts/build-schematics.ts index 5e952484158..3a14d2f03cb 100644 --- a/npm/ng-packs/scripts/build-schematics.ts +++ b/npm/ng-packs/scripts/build-schematics.ts @@ -20,7 +20,9 @@ class FileCopy { const PACKAGE_TO_BUILD = 'schematics'; const FILES_TO_COPY_AFTER_BUILD: (FileCopy | string)[] = [ - { src: 'src/commands/proxy/schema.json', dest: 'commands/proxy/schema.json' }, + { src: 'src/commands/proxy-add/schema.json', dest: 'commands/proxy-add/schema.json' }, + { src: 'src/commands/proxy-refresh/schema.json', dest: 'commands/proxy-refresh/schema.json' }, + { src: 'src/commands/proxy-remove/schema.json', dest: 'commands/proxy-remove/schema.json' }, { src: 'src/commands/api/files-enum', dest: 'commands/api/files-enum' }, { src: 'src/commands/api/files-model', dest: 'commands/api/files-model' }, { src: 'src/commands/api/files-service', dest: 'commands/api/files-service' },