-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[GITEA] add new gitea service (release/languages) (#9781)
* add gitea service based on gitlab * update gitea to use mocks * add gitea release test * move tests to use public repo on codeberg and fixes * add pagination, update tests to live, set gitea_url as required * add auth test (wip) * fix base auth test * fix required optionalUrl, remove default, assume semver from firstpage * update example to use stable repository
- Loading branch information
1 parent
2814de2
commit 8f1f787
Showing
9 changed files
with
381 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { BaseJsonService } from '../index.js' | ||
|
||
export default class GiteaBase extends BaseJsonService { | ||
static auth = { | ||
passKey: 'gitea_token', | ||
serviceKey: 'gitea', | ||
} | ||
|
||
async fetch({ url, options, schema, httpErrors }) { | ||
return this._requestJson( | ||
this.authHelper.withBearerAuthHeader({ | ||
schema, | ||
url, | ||
options, | ||
httpErrors, | ||
}), | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import Joi from 'joi' | ||
import { expect } from 'chai' | ||
import nock from 'nock' | ||
import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js' | ||
import GiteaBase from './gitea-base.js' | ||
|
||
class DummyGiteaService extends GiteaBase { | ||
static route = { base: 'fake-base' } | ||
|
||
async handle() { | ||
const data = await this.fetch({ | ||
schema: Joi.any(), | ||
url: 'https://codeberg.org/api/v1/repos/CanisHelix/shields-badge-test/releases', | ||
}) | ||
return { message: data.message } | ||
} | ||
} | ||
|
||
describe('GiteaBase', function () { | ||
describe('auth', function () { | ||
cleanUpNockAfterEach() | ||
|
||
const config = { | ||
public: { | ||
services: { | ||
gitea: { | ||
authorizedOrigins: ['https://codeberg.org'], | ||
}, | ||
}, | ||
}, | ||
private: { | ||
gitea_token: 'fake-key', | ||
}, | ||
} | ||
|
||
it('sends the auth information as configured', async function () { | ||
const scope = nock('https://codeberg.org') | ||
.get('/api/v1/repos/CanisHelix/shields-badge-test/releases') | ||
.matchHeader('Authorization', 'Bearer fake-key') | ||
.reply(200, { message: 'fake message' }) | ||
expect( | ||
await DummyGiteaService.invoke(defaultContext, config, {}), | ||
).to.not.have.property('isError') | ||
|
||
scope.done() | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
const documentation = ` | ||
Note that the gitea_url parameter is required because there is canonical hosted gitea service provided by Gitea. | ||
` | ||
|
||
function httpErrorsFor() { | ||
return { | ||
403: 'private repo', | ||
404: 'user or repo not found', | ||
} | ||
} | ||
|
||
export { documentation, httpErrorsFor } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import Joi from 'joi' | ||
import { nonNegativeInteger, optionalUrl } from '../validators.js' | ||
import { metric } from '../text-formatters.js' | ||
import { pathParam, queryParam } from '../index.js' | ||
import { documentation, httpErrorsFor } from './gitea-helper.js' | ||
import GiteaBase from './gitea-base.js' | ||
|
||
/* | ||
We're expecting a response like { "Python": 39624, "Shell": 104 } | ||
The keys could be anything and {} is a valid response (e.g: for an empty repo) | ||
*/ | ||
const schema = Joi.object().pattern(/./, nonNegativeInteger) | ||
|
||
const queryParamSchema = Joi.object({ | ||
gitea_url: optionalUrl.required(), | ||
}).required() | ||
|
||
export default class GiteaLanguageCount extends GiteaBase { | ||
static category = 'analysis' | ||
|
||
static route = { | ||
base: 'gitea/languages/count', | ||
pattern: ':user/:repo', | ||
queryParamSchema, | ||
} | ||
|
||
static openApi = { | ||
'/gitea/languages/count/{user}/{repo}': { | ||
get: { | ||
summary: 'Gitea language count', | ||
description: documentation, | ||
parameters: [ | ||
pathParam({ | ||
name: 'user', | ||
example: 'forgejo', | ||
}), | ||
pathParam({ | ||
name: 'repo', | ||
example: 'forgejo', | ||
}), | ||
queryParam({ | ||
name: 'gitea_url', | ||
example: 'https://codeberg.org', | ||
required: true, | ||
}), | ||
], | ||
}, | ||
}, | ||
} | ||
|
||
static defaultBadgeData = { label: 'languages' } | ||
|
||
static render({ languagesCount }) { | ||
return { | ||
message: metric(languagesCount), | ||
color: 'blue', | ||
} | ||
} | ||
|
||
async fetch({ user, repo, baseUrl }) { | ||
// https://try.gitea.io/api/swagger#/repository/repoGetLanguages | ||
return super.fetch({ | ||
schema, | ||
url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`, | ||
httpErrors: httpErrorsFor('user or repo not found'), | ||
}) | ||
} | ||
|
||
async handle({ user, repo }, { gitea_url: baseUrl }) { | ||
const data = await this.fetch({ | ||
user, | ||
repo, | ||
baseUrl, | ||
}) | ||
return this.constructor.render({ languagesCount: Object.keys(data).length }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Joi from 'joi' | ||
import { createServiceTester } from '../tester.js' | ||
|
||
export const t = await createServiceTester() | ||
|
||
t.create('language count (empty repo)') | ||
.get( | ||
'/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org', | ||
) | ||
.expectBadge({ | ||
label: 'languages', | ||
message: '0', | ||
}) | ||
|
||
t.create('language count') | ||
.get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org') | ||
.expectBadge({ | ||
label: 'languages', | ||
message: Joi.number().integer().positive(), | ||
}) | ||
|
||
t.create('language count (user or repo not found)') | ||
.get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org') | ||
.expectBadge({ | ||
label: 'languages', | ||
message: 'user or repo not found', | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import Joi from 'joi' | ||
import { optionalUrl } from '../validators.js' | ||
import { latest, renderVersionBadge } from '../version.js' | ||
import { NotFound, pathParam, queryParam } from '../index.js' | ||
import { documentation, httpErrorsFor } from './gitea-helper.js' | ||
import GiteaBase from './gitea-base.js' | ||
|
||
const schema = Joi.array().items( | ||
Joi.object({ | ||
name: Joi.string().required(), | ||
tag_name: Joi.string().required(), | ||
prerelease: Joi.boolean().required(), | ||
}), | ||
) | ||
|
||
const sortEnum = ['date', 'semver'] | ||
const displayNameEnum = ['tag', 'release'] | ||
const dateOrderByEnum = ['created_at', 'published_at'] | ||
|
||
const queryParamSchema = Joi.object({ | ||
gitea_url: optionalUrl.required(), | ||
include_prereleases: Joi.equal(''), | ||
sort: Joi.string() | ||
.valid(...sortEnum) | ||
.default('date'), | ||
display_name: Joi.string() | ||
.valid(...displayNameEnum) | ||
.default('tag'), | ||
date_order_by: Joi.string() | ||
.valid(...dateOrderByEnum) | ||
.default('created_at'), | ||
}).required() | ||
|
||
export default class GiteaRelease extends GiteaBase { | ||
static category = 'version' | ||
|
||
static route = { | ||
base: 'gitea/v/release', | ||
pattern: ':user/:repo', | ||
queryParamSchema, | ||
} | ||
|
||
static openApi = { | ||
'/gitea/v/release/{user}/{repo}': { | ||
get: { | ||
summary: 'Gitea Release', | ||
description: documentation, | ||
parameters: [ | ||
pathParam({ | ||
name: 'user', | ||
example: 'forgejo', | ||
}), | ||
pathParam({ | ||
name: 'repo', | ||
example: 'forgejo', | ||
}), | ||
queryParam({ | ||
name: 'gitea_url', | ||
example: 'https://codeberg.org', | ||
required: true, | ||
}), | ||
queryParam({ | ||
name: 'include_prereleases', | ||
schema: { type: 'boolean' }, | ||
example: null, | ||
}), | ||
queryParam({ | ||
name: 'sort', | ||
schema: { type: 'string', enum: sortEnum }, | ||
example: 'semver', | ||
}), | ||
queryParam({ | ||
name: 'display_name', | ||
schema: { type: 'string', enum: displayNameEnum }, | ||
example: 'release', | ||
}), | ||
queryParam({ | ||
name: 'date_order_by', | ||
schema: { type: 'string', enum: dateOrderByEnum }, | ||
example: 'created_at', | ||
}), | ||
], | ||
}, | ||
}, | ||
} | ||
|
||
static defaultBadgeData = { label: 'release' } | ||
|
||
async fetch({ user, repo, baseUrl }) { | ||
// https://try.gitea.io/api/swagger#/repository/repoGetRelease | ||
return super.fetch({ | ||
schema, | ||
url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`, | ||
httpErrors: httpErrorsFor(), | ||
}) | ||
} | ||
|
||
static transform({ releases, isSemver, includePrereleases, displayName }) { | ||
if (releases.length === 0) { | ||
throw new NotFound({ prettyMessage: 'no releases found' }) | ||
} | ||
|
||
const displayKey = displayName === 'tag' ? 'tag_name' : 'name' | ||
|
||
if (isSemver) { | ||
return latest( | ||
releases.map(t => t[displayKey]), | ||
{ pre: includePrereleases }, | ||
) | ||
} | ||
|
||
if (!includePrereleases) { | ||
const stableReleases = releases.filter(release => !release.prerelease) | ||
if (stableReleases.length > 0) { | ||
return stableReleases[0][displayKey] | ||
} | ||
} | ||
|
||
return releases[0][displayKey] | ||
} | ||
|
||
async handle( | ||
{ user, repo }, | ||
{ | ||
gitea_url: baseUrl, | ||
include_prereleases: pre, | ||
sort, | ||
display_name: displayName, | ||
date_order_by: orderBy, | ||
}, | ||
) { | ||
const isSemver = sort === 'semver' | ||
const releases = await this.fetch({ | ||
user, | ||
repo, | ||
baseUrl, | ||
isSemver, | ||
}) | ||
const version = this.constructor.transform({ | ||
releases, | ||
isSemver, | ||
includePrereleases: pre !== undefined, | ||
displayName, | ||
}) | ||
return renderVersionBadge({ version }) | ||
} | ||
} |
Oops, something went wrong.