diff --git a/README.md b/README.md index 1b0c30a4..9f023616 100644 --- a/README.md +++ b/README.md @@ -194,11 +194,4 @@ SELECT Sentences.* ## API -| Purpose | Endpoint | Example | -| :------------ | :----------- | :---------- | -| Get info for a single sentence. Replace `:locale` with the locale code for the language. In the browser you can just copy/paste the sentence and it will correctly handle spaces and symbols). | `/sentence-collector/sentences/:locale` | `/sentence-collector/sentences/de?sentence=Wie%20zuverl%C3%A4ssig%20eine%20Versicherung%20zahlt,%20wei%C3%9F%20man%20erst,%20wenn%20man%20sie%20braucht.` | -| Get all sources. Replace `:locale` with the locale code for the language. | `/sentence-collector/sentences/sources/:locale` | `/sentence-collector/sentences/sources/de` | -| Get all sentences as text. Replace `:locale` with the locale code for the language. | `/sentence-collector/sentences/text/:locale` | `/sentence-collector/sentences/text/de` | -| Get all approved sentences as text. Replace `:locale` with the locale code for the language. | `/sentence-collector/sentences/text/approved/:locale` | `/sentence-collector/sentences/text/approved/de` | -| Get all undecided sentences as text. Replace `:locale` with the locale code for the language. | `/sentence-collector/sentences/text/undecided/:locale` | `/sentence-collector/sentences/text/undecided/de` | -| Get all rejected sentences as text. Replace `:locale` with the locale code for the language. | `/sentence-collector/sentences/text/rejected/:locale` | `/sentence-collector/sentences/text/rejected/de` | +You can find the API documentation for the public endpoints in the [OpenAPI spec](https://commonvoice.mozilla.org/sentence-collector/api). diff --git a/server/app.js b/server/app.js index 6fbedbc7..3a5972e8 100644 --- a/server/app.js +++ b/server/app.js @@ -9,6 +9,10 @@ const session = require('express-session'); const path = require('path'); const passport = require('passport'); const Auth0Strategy = require('passport-auth0'); +const swaggerUi = require('swagger-ui-express'); +const YAML = require('yamljs'); +const swaggerDocument = YAML.load('./swagger.yml'); + const models = require('./lib/models'); const users = require('./lib/users'); const sentencesRoutes = require('./routes/sentences'); @@ -141,6 +145,7 @@ app.use(`${MOUNT_PATH}/sentences`, sentencesRoutes); app.use(`${MOUNT_PATH}/stats`, statsRoutes); app.use(`${MOUNT_PATH}/users`, usersRoutes); app.use(`${MOUNT_PATH}/votes`, votesRoutes); +app.use('/api', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); app.use(express.static(path.resolve(__dirname, '..', 'web', 'dist'))); module.exports = app; diff --git a/server/package-lock.json b/server/package-lock.json index 5efcb4ff..b403edbc 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -675,7 +675,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -5193,8 +5192,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sqlstring": { "version": "2.3.2", @@ -5415,6 +5413,19 @@ "has-flag": "^4.0.0" } }, + "swagger-ui-dist": { + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.45.0.tgz", + "integrity": "sha512-SUqZbt0WQytjaI/bUr2gn8IqFE+Te6Tl4uA7n4xtylRdXKXmMm5s/mfTtvpnleHBjxcmQ6ONTA9ilN4iOxMulQ==" + }, + "swagger-ui-express": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz", + "integrity": "sha512-Xs2BGGudvDBtL7RXcYtNvHsFtP1DBFPMJFRxHe5ez/VG/rzVOEjazJOOSc/kSCyxreCTKfJrII6MJlL9a6t8vw==", + "requires": { + "swagger-ui-dist": "^3.18.1" + } + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -5995,6 +6006,15 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "requires": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + } + }, "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", diff --git a/server/package.json b/server/package.json index c6ba7f03..26248b93 100644 --- a/server/package.json +++ b/server/package.json @@ -27,8 +27,10 @@ "passport-auth0": "^1.4.0", "sequelize": "^6.3.5", "sequelize-cli": "^6.2.0", + "swagger-ui-express": "^4.1.6", "talisman": "^1.1.2", - "uuid": "^8.3.1" + "uuid": "^8.3.1", + "yamljs": "^0.3.0" }, "devDependencies": { "ava": "^3.13.0", diff --git a/server/swagger.yml b/server/swagger.yml new file mode 100644 index 00000000..40f390a3 --- /dev/null +++ b/server/swagger.yml @@ -0,0 +1,149 @@ +swagger: "2.0" +info: + description: "These are public endpoints for the Common Voice Sentence Collector." + version: "1.0.0" + title: "Sentence Collector" +host: "commonvoice.mozilla.org" +basePath: "/sentence-collector" +schemes: +- "https" +paths: + /sentences/{localeId}: + get: + summary: "Get all sentences for a language" + description: "" + produces: + - "application/json" + parameters: + - in: "path" + name: "localeId" + type: "string" + description: "Locale ISO code to query for" + required: true + - in: "query" + name: "sentence" + type: "string" + description: "Sentence to filter for" + responses: + "200": + description: "Success" + schema: + $ref: "#/definitions/SentencesList" + /sentences/sources/{localeId}: + get: + summary: "Get all sources as list of text for a language" + description: "" + produces: + - "text/plain" + parameters: + - in: "path" + name: "localeId" + type: "string" + description: "Locale ISO code to query for" + required: true + responses: + "200": + description: "Success" + schema: + $ref: "#/definitions/SourceText" + /sentences/text/{localeId}: + get: + summary: "Get all sentences as list of text for a language" + description: "" + produces: + - "text/plain" + parameters: + - in: "path" + name: "localeId" + type: "string" + description: "Locale ISO code to query for" + required: true + responses: + "200": + description: "Success" + schema: + $ref: "#/definitions/Text" + /sentences/text/approved/{localeId}: + get: + summary: "Get all approved sentences as list of text for a language" + description: "" + produces: + - "text/plain" + parameters: + - in: "path" + name: "localeId" + type: "string" + description: "Locale ISO code to query for" + required: true + responses: + "200": + description: "Success" + schema: + $ref: "#/definitions/Text" + /sentences/text/undecided/{localeId}: + get: + summary: "Get all undecided sentences as list of text for a language" + description: "" + produces: + - "text/plain" + parameters: + - in: "path" + name: "localeId" + type: "string" + description: "Locale ISO code to query for" + required: true + responses: + "200": + description: "Success" + schema: + $ref: "#/definitions/Text" + /sentences/text/rejected/{localeId}: + get: + summary: "Get all undecided sentences as list of text for a language" + description: "" + produces: + - "text/plain" + parameters: + - in: "path" + name: "localeId" + type: "string" + description: "Locale ISO code to query for" + required: true + responses: + "200": + description: "Success" + schema: + $ref: "#/definitions/Text" + +definitions: + SentencesList: + type: "array" + items: + $ref: "#/definitions/Sentence" + Sentence: + type: "object" + properties: + id: + type: "integer" + format: "int64" + sentence: + type: "string" + source: + type: "string" + batch: + type: "string" + format: "uuid" + localeId: + type: "string" + createdAt: + type: "string" + format: "date-time" + updatedAt: + type: "string" + format: "date-time" + Text: + type: "string" + example: "Sentence One\nSentence Two" + SourceText: + type: "string" + example: "Source A\nSource B"