Skip to content

Commit 72f69bc

Browse files
Incremental indexing support for api
1 parent bf1d041 commit 72f69bc

File tree

11 files changed

+416
-108
lines changed

11 files changed

+416
-108
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ API_DOCS_PATH=../ember-jsonapi-docs/tmp
44
GUIDES_DOCS_PATH=../guides-app/dist/content
55
DEBUG=false
66
DRIVER=algolia
7+
AWS_ACCESS_KEY=""
8+
AWS_SECRET_KEY=""

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ node_modules
33
build
44
drivers-output
55
dist
6+
yarn-error.log
7+
tmp
8+
.vscode

.prettierrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"singleQuote": true,
3-
"semi": false
3+
"semi": false,
4+
"trailingComma": "es5"
45
}

index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ program
99
.version(version, '-v, --version')
1010
.description(description)
1111
.option('-p, --project <project>', 'Project name. Accepts "api" or "guides"')
12+
.option(
13+
'-c, --clear-index',
14+
'Whether indexes of the project should be cleared while processing'
15+
)
1216

1317
program.on('--help', function() {
1418
console.log(`
@@ -25,7 +29,7 @@ switch (program.project) {
2529
runGuides()
2630
break
2731
case 'api':
28-
runApi()
32+
runApi(program.clearIndex)
2933
break
3034
default:
3135
throw new Error('Invalid --project property')

lib/api-docs-sync.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Promise, all as waitForAllPromises } from 'bluebird'
2+
import S3 from 's3'
3+
import ora from 'ora'
4+
import humanSize from 'human-size'
5+
import http from 'http'
6+
import https from 'https'
7+
8+
// To increase s3's download & upload dir perf
9+
http.globalAgent.maxSockets = https.globalAgent.maxSockets = 30
10+
11+
const { AWS_ACCESS_KEY, AWS_SECRET_KEY } = process.env
12+
13+
const client = S3.createClient({
14+
s3Options: { accessKeyId: AWS_ACCESS_KEY, secretAccessKey: AWS_SECRET_KEY }
15+
})
16+
17+
const jsonDocsDirDownloadOptions = {
18+
localDir: 'tmp/json-docs',
19+
s3Params: { Bucket: 'api-docs.emberjs.com', Prefix: 'json-docs' }
20+
}
21+
22+
let revDocsDirDownloadOptions = {
23+
localDir: 'tmp/rev-index',
24+
s3Params: { Bucket: 'api-docs.emberjs.com', Prefix: 'rev-index' }
25+
}
26+
27+
const syncDir = options => {
28+
return new Promise((resolve, reject) => {
29+
let sync = client.downloadDir(options)
30+
let progressIndicator = ora(
31+
`downloading ${options.s3Params.Prefix} docs`
32+
).start()
33+
34+
sync.on('progress', () => {
35+
const { progressAmount, progressTotal } = sync
36+
progressIndicator.text = `Downloading json docs (${humanSize(
37+
progressAmount
38+
)} of ${humanSize(progressTotal)})`
39+
})
40+
41+
sync.on('end', () => {
42+
progressIndicator.succeed(`downloaded ${options.s3Params.Prefix} docs`)
43+
resolve()
44+
})
45+
46+
sync.on('error', err => {
47+
progressIndicator.fail()
48+
reject(err)
49+
})
50+
})
51+
}
52+
53+
export default function downloadExistingDocsToLocal() {
54+
return waitForAllPromises([
55+
syncDir(jsonDocsDirDownloadOptions),
56+
syncDir(revDocsDirDownloadOptions)
57+
])
58+
}

lib/api.js

Lines changed: 80 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
require('dotenv').config()
22

3-
import Bluebird from 'bluebird'
3+
import { all, resolve } from 'bluebird'
4+
import { compare as compareSemVers } from 'semver'
5+
import { difference } from 'lodash'
6+
47
import logger from './utils/logger'
58
import drivers from './drivers'
69
import { readTmpFileFactory, readTmpFileAsyncFactory } from './utils/fs'
710
import schemas from './schemas'
11+
import downloadApiDocs from './api-docs-sync'
812

913
// Get 'readTmpFile' and 'readTmpFileAsync' bound by 'api'
1014
const PROJECT_TYPE = 'api'
@@ -15,46 +19,68 @@ const { DRIVER } = process.env
1519

1620
const SelectedDriver = drivers[DRIVER]
1721

18-
export function run() {
19-
// Initialise drivers
20-
SelectedDriver.init('modules')
21-
SelectedDriver.init('classes')
22-
SelectedDriver.init('methods')
23-
24-
// Load ember.json which includes all available ember versions.
25-
return (
26-
readTmpFileAsync('rev-index/ember.json')
27-
// Extract available versions
28-
.then(emberJson => emberJson.meta.availableVersions)
29-
// Clear the driver contents
30-
.tap(clearDriver)
31-
// Grab the json file of each ember version
32-
.map(readEmberIndexFileForVersion)
22+
const apiIndexes = ['modules', 'classes', 'methods', 'versions']
23+
24+
export async function run(clearIndex = false) {
25+
apiIndexes.map(SelectedDriver.init)
26+
27+
if (clearIndex) {
28+
await all(apiIndexes.map(SelectedDriver.clear))
29+
}
30+
31+
await downloadApiDocs()
32+
33+
await all([processDocs('ember'), processDocs('ember-data')])
34+
}
35+
36+
async function processDocs(project) {
37+
let prevIndexedVersions = await SelectedDriver.getPreviouslyIndexedVersions(
38+
project
39+
)
40+
41+
const {
42+
meta: { availableVersions }
43+
} = await readTmpFileAsync(`rev-index/${project}.json`)
44+
45+
let versionsToProcess = difference(availableVersions, prevIndexedVersions)
46+
47+
if (versionsToProcess.length === 0) {
48+
console.log(`No new versions to process for ${project}`)
49+
return
50+
}
51+
52+
try {
53+
console.log(`Processing ${project} for versions: ${versionsToProcess}`)
54+
55+
await versionsToProcess
56+
.map(version => readIndexFileForVersion(version, project))
3357
// Fetch all public modules and public classes
34-
.map(fetchPublicModuleClassesForEmberVersion)
58+
.map(versionIndexObject =>
59+
fetchPublicModuleClassesForVersion(versionIndexObject, project)
60+
)
3561
// Run the schema against all data stored
3662
.map(mapDataForVersion)
3763
// Write out to selected driver.
3864
.map(writeToDriver)
39-
// Load ember-data.json which includes all available ember-data versions
40-
.then(() => readTmpFileAsync('rev-index/ember-data.json'))
41-
.then(emberJson => emberJson.meta.availableVersions)
42-
.map(readEmberDataIndexFileForVersion)
43-
.map(fetchPublicModuleClassesForEmberDataVersion)
44-
.map(mapDataForVersion)
45-
.map(writeToDriver)
46-
// Handle script error
47-
.catch(errorHandler)
48-
)
49-
}
5065

51-
function readEmberIndexFileForVersion(version) {
52-
return readIndexFileForVersion(version, 'ember')
66+
await SelectedDriver.write(
67+
'versions',
68+
[
69+
{
70+
id: project,
71+
name: project,
72+
versions: [...prevIndexedVersions, ...versionsToProcess].sort(
73+
compareSemVers
74+
)
75+
}
76+
],
77+
project
78+
)
79+
} catch (err) {
80+
console.log('Error:: ', err)
81+
}
5382
}
5483

55-
function readEmberDataIndexFileForVersion(version) {
56-
return readIndexFileForVersion(version, 'ember-data')
57-
}
5884
/**
5985
* Read index file for version
6086
*
@@ -68,14 +94,6 @@ function readIndexFileForVersion(version, libName) {
6894
return readTmpFile(emberVersionJSONPath)
6995
}
7096

71-
function fetchPublicModuleClassesForEmberVersion(versionIndexObject) {
72-
return fetchPublicModuleClassesForVersion(versionIndexObject, 'ember')
73-
}
74-
75-
function fetchPublicModuleClassesForEmberDataVersion(versionIndexObject) {
76-
return fetchPublicModuleClassesForVersion(versionIndexObject, 'ember-data')
77-
}
78-
7997
/**
8098
* Fetch public modules and classes for version
8199
*
@@ -143,40 +161,37 @@ function mapDataForVersion(versionObject) {
143161
* @param versionObject - Object version to write out
144162
*/
145163
function writeToDriver(versionObject) {
164+
const { id } = versionObject.version.data
165+
166+
let tokens = id.split('-')
167+
let version = tokens.pop()
168+
let projectName = tokens.join('-')
169+
146170
logger.logGreen(
147-
`version: ${versionObject.version.data.id}, public classes: ${
171+
`version: ${id}, public classes: ${
148172
versionObject.publicClasses.length
149173
}, public modules: ${versionObject.publicModules.length}, methods: ${
150174
versionObject.methods.length
151175
}`
152176
)
153177

154-
// Wait for all promises to complete before continuing
155-
return Bluebird.all([
156-
SelectedDriver.write('modules', versionObject.publicModules),
157-
SelectedDriver.write('classes', versionObject.publicClasses),
158-
SelectedDriver.write('methods', versionObject.methods)
178+
return all([
179+
SelectedDriver.write(
180+
'modules',
181+
versionObject.publicModules,
182+
projectName,
183+
version
184+
),
185+
SelectedDriver.write(
186+
'classes',
187+
versionObject.publicClasses,
188+
projectName,
189+
version
190+
),
191+
SelectedDriver.write('methods', versionObject.methods, projectName, version)
159192
])
160193
}
161194

162-
/**
163-
* Clears the driver indices
164-
*
165-
* @returns {Promise} - Promise with all drivers cleared.
166-
*/
167-
function clearDriver() {
168-
return Bluebird.all([
169-
SelectedDriver.clear('modules'),
170-
SelectedDriver.clear('classes'),
171-
SelectedDriver.clear('methods')
172-
])
173-
}
174-
175-
// Handle errors
176-
function errorHandler(err) {
177-
console.log('Error:: ', err)
178-
}
179-
180195
/**
181196
* Takes an array of classes, extracts the methods from each one,
182197
* and runs the method schema to transform the payload

lib/drivers/algolia.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ let client = null
99

1010
const indices = {}
1111

12-
export function init(indexName) {
12+
function init(indexName) {
1313
if (!client) client = AlgoliaSearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY)
1414
// Create an algolia index
1515
const index = client.initIndex(indexName)
@@ -19,7 +19,7 @@ export function init(indexName) {
1919
return index
2020
}
2121

22-
export function write(indexName, records) {
22+
function write(indexName, records) {
2323
const index = indices[indexName]
2424

2525
return Bluebird.resolve(index.addObjects(records))
@@ -36,13 +36,18 @@ export function write(indexName, records) {
3636
})
3737
}
3838

39-
export function clear(indexName) {
39+
function clear(indexName) {
4040
const index = indices[indexName]
4141
return Bluebird.resolve(index.clearIndex())
4242
}
4343

44-
export default {
45-
init,
46-
write,
47-
clear
44+
async function getPreviouslyIndexedVersions(projectName) {
45+
let { hits } = await indices['versions'].search(projectName)
46+
if (!hits) {
47+
return []
48+
}
49+
let { versions } = hits.find(hit => hit.name === projectName)
50+
return versions
4851
}
52+
53+
export default { init, write, clear, getPreviouslyIndexedVersions }

lib/drivers/json.js

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,40 @@
1-
import Bluebird from 'bluebird'
2-
import fs from 'file-system'
1+
import { resolve } from 'bluebird'
2+
import {
3+
emptyDirSync,
4+
existsSync,
5+
outputJsonSync,
6+
readJsonSync,
7+
rmdirSync,
8+
} from 'fs-extra'
39

4-
export function init(fileName) {}
10+
const outputFolder = './drivers-output'
511

6-
export function write(fileName, content) {
7-
return fs.writeFileSync(
8-
`./drivers-output/json/${fileName}.json`,
9-
JSON.stringify(content),
10-
'utf8'
11-
)
12+
function init() {}
13+
14+
function write(indexName, content, projectName, version) {
15+
let fileName = `${outputFolder}/json/${projectName}/`
16+
fileName += version ? `${version}/${indexName}.json` : `${indexName}.json`
17+
18+
return outputJsonSync(fileName, content, { spaces: 2 })
19+
}
20+
21+
function clear() {
22+
if (!existsSync(outputFolder)) {
23+
return resolve()
24+
}
25+
emptyDirSync(outputFolder)
26+
return resolve(rmdirSync(outputFolder))
1227
}
1328

14-
export function clear() {
15-
return Bluebird.resolve()
29+
function getPreviouslyIndexedVersions(projectName) {
30+
let fileName = `${outputFolder}/json/${projectName}/versions.json`
31+
32+
if (!existsSync(fileName)) {
33+
return []
34+
}
35+
36+
let [{ versions }] = readJsonSync(fileName)
37+
return versions
1638
}
1739

18-
export default { init, write, clear }
40+
export default { init, write, clear, getPreviouslyIndexedVersions }

0 commit comments

Comments
 (0)