1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const { ApiCollector, MultiProjectApiCollector } = require('./api_collector');
17const { Logger } = require('./utils');
18const path = require('path');
19const fs = require('fs');
20
21class AppApiCollectorPlugin {
22  constructor() {
23    this.logTag = 'AppApiCollectorPlugin';
24  }
25  getPluginOptions() {
26    return {
27      name: 'api-collector',
28      version: '0.1.0',
29      description: 'collect api from app\'s source code.',
30      commands: [
31        {
32          isRequiredOption: false,
33          options: ['--app <string>', 'app root directory'],
34        },
35        {
36          isRequiredOption: false,
37          options: ['--appDir <string>', 'a path that contains multiple applications'],
38        },
39        {
40          isRequiredOption: false,
41          options: ['--sdk <string>', 'sdk path, need to specify the ets directory, e.g sdk-root/version/ets'],
42        },
43        {
44          isRequiredOption: false,
45          options: ['--sdkRoot <string>', 'sdk root path'],
46        },
47        {
48          isRequiredOption: false,
49          options: ['--output <string>', 'the path to output the report'],
50        },
51        {
52          isRequiredOption: false,
53          options: ['--format <json,excel>', 'format of the output report'],
54        },
55        {
56          isRequiredOption: false,
57          options: ['--scanTest', 'scan ohosTest'],
58        },
59        {
60          isRequiredOption: false,
61          options: ['--debug', 'output debug logs'],
62        },
63        {
64          isRequiredOption: false,
65          options: ['--noRepeat', 'apiInfos is not repeat']
66        }
67      ],
68    };
69  }
70
71  async start(argv) {
72    if (!this.checkArguments(argv)) {
73      return;
74    }
75    const startTime = Date.now();
76    if (argv.app) {
77      await this.scanSingleProject(argv);
78    } else if (argv.appDir) {
79      await this.scanMultiProject(argv);
80    } else if (argv.dir) {
81      await this.scanNonProject(argv);
82    } else {
83      Logger.info(this.logTag, 'see --help');
84    }
85    Logger.info(this.logTag, `elapsed time ${Date.now() - startTime}`);
86    if (argv.debug) {
87      Logger.flush(this.getLogPath(argv));
88    }
89  }
90
91  async scanSingleProject(argv) {
92    const collector = new ApiCollector(argv);
93    await collector.setLibPath(this.findLibPath()).setIncludeTest(argv.scanTest).start();
94  }
95
96  async scanMultiProject(argv) {
97    if (!argv.sdk) {
98      const collector = new MultiProjectApiCollector(argv);
99      await collector.setLibPath(this.findLibPath()).setIncludeTest(argv.scanTest).start();
100    } else {
101      Logger.error(this.logTag, '--appDir and --sdkRoot are used together, replace --sdk with --sdkRoot');
102    }
103  }
104
105  async scanNonProject(argv) {
106    if (!argv.sdk) {
107      Logger.error(this.logTag, 'the --sdk is required when scanning non-project');
108      return;
109    }
110    const apiCollector = new ApiCollector(argv);
111    await apiCollector.setLibPath(this.findLibPath()).start();
112  }
113
114  getLogPath(argv) {
115    if (argv.output) {
116      return argv.output;
117    }
118    if (argv.appDir) {
119      return argv.appDir;
120    }
121    if (argv.app) {
122      return argv.app;
123    }
124    return __dirname;
125  }
126
127  findLibPath() {
128    if (process.env.bundleMode) {
129      return path.resolve(__dirname, 'libs');
130    }
131    return path.resolve(__dirname, '..', 'libs');
132  }
133
134  stop() {
135
136  }
137
138  checkArguments(argv) {
139    if (argv.sdk) {
140      const apiPath = path.resolve(argv.sdk, 'api');
141      const componentPath = path.resolve(argv.sdk, 'component');
142      if (!fs.existsSync(apiPath) || !fs.existsSync(componentPath)) {
143        Logger.error(this.logTag, '--sdk option need to specify the ets directory');
144        return false;
145      }
146    }
147    return this.checkPathIsValid(argv.app) &&
148      this.checkPathIsValid(argv.output) &&
149      this.checkPathIsValid(argv.sdkRoot) &&
150      this.checkPathIsValid(argv.appDir);
151  }
152
153  checkPathIsValid(path) {
154    if (path && !fs.existsSync(path)) {
155      Logger.error(this.logTag, `${path} not exists`);
156      return false;
157    }
158    return true;
159  }
160}
161
162module.exports = AppApiCollectorPlugin;