1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-11-08 23:25:51 +01:00

chore: list stars of an user (#3451)

* chore: list stars of user

* chore: add pnpm test e2e
This commit is contained in:
Juan Picado 2022-10-21 23:05:18 +02:00 committed by GitHub
parent aecad09c6a
commit a8e1ffd72f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 278 additions and 173 deletions

@ -20,6 +20,7 @@
| ping | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| star | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| stars | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| dist-tag | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
> notes:

@ -63,6 +63,22 @@ describe('star a package', () => {
expect(resp1.stdout).toEqual(`${pkgName}`);
});
test('should list stars of a user %s', async () => {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
await npm({ cwd: tempFolder }, 'star', pkgName, ...addRegistry(registry.getRegistryUrl()));
const resp = await npm({ cwd: tempFolder }, 'stars', ...addRegistry(registry.getRegistryUrl()));
// side effects: this result is affected the the package published in the previous step
expect(resp.stdout).toEqual(`@verdaccio/foo@verdaccio/stars`);
});
afterAll(async () => {
registry.stop();
});

@ -63,6 +63,22 @@ describe('star a package', () => {
expect(resp1.stdout).toEqual(`${pkgName}`);
});
test('should list stars of a user %s', async () => {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
await npm({ cwd: tempFolder }, 'star', pkgName, ...addRegistry(registry.getRegistryUrl()));
const resp = await npm({ cwd: tempFolder }, 'stars', ...addRegistry(registry.getRegistryUrl()));
// side effects: this result is affected the the package published in the previous step
expect(resp.stdout).toEqual(`@verdaccio/foo@verdaccio/stars`);
});
afterAll(async () => {
registry.stop();
});

@ -63,6 +63,22 @@ describe('star a package', () => {
expect(resp1.stdout).toEqual(`${pkgName}`);
});
test('should list stars of a user %s', async () => {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
await npm({ cwd: tempFolder }, 'star', pkgName, ...addRegistry(registry.getRegistryUrl()));
const resp = await npm({ cwd: tempFolder }, 'stars', ...addRegistry(registry.getRegistryUrl()));
// side effects: this result is affected the the package published in the previous step
expect(resp.stdout).toEqual(`@verdaccio/foo@verdaccio/stars`);
});
afterAll(async () => {
registry.stop();
});

@ -63,6 +63,22 @@ describe('star a package', () => {
expect(resp1.stdout).toEqual(`${pkgName}`);
});
test('should list stars of a user %s', async () => {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
await npm({ cwd: tempFolder }, 'star', pkgName, ...addRegistry(registry.getRegistryUrl()));
const resp = await npm({ cwd: tempFolder }, 'stars', ...addRegistry(registry.getRegistryUrl()));
// side effects: this result is affected the the package published in the previous step
expect(resp.stdout).toEqual(`@verdaccio/foo@verdaccio/stars`);
});
afterAll(async () => {
registry.stop();
});

@ -63,6 +63,26 @@ describe('star a package', () => {
expect(resp1.stdout).toEqual(`${pkgName}`);
});
test('should list stars of a user %s', async () => {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await pnpmUtils.publish(pnpm, tempFolder, pkgName, registry);
await pnpm({ cwd: tempFolder }, 'star', pkgName, ...addRegistry(registry.getRegistryUrl()));
const resp = await pnpm(
{ cwd: tempFolder },
'stars',
...addRegistry(registry.getRegistryUrl())
);
// side effects: this result is affected the the package published in the previous step
expect(resp.stdout).toEqual(`@verdaccio/foo@verdaccio/stars`);
});
afterAll(async () => {
registry.stop();
});

@ -63,6 +63,26 @@ describe('star a package', () => {
expect(resp1.stdout).toEqual(`${pkgName}`);
});
test('should list stars of a user %s', async () => {
const pkgName = '@verdaccio/stars';
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await pnpmUtils.publish(pnpm, tempFolder, pkgName, registry);
await pnpm({ cwd: tempFolder }, 'star', pkgName, ...addRegistry(registry.getRegistryUrl()));
const resp = await pnpm(
{ cwd: tempFolder },
'stars',
...addRegistry(registry.getRegistryUrl())
);
// side effects: this result is affected the the package published in the previous step
expect(resp.stdout).toEqual(`@verdaccio/foo@verdaccio/stars`);
});
afterAll(async () => {
registry.stop();
});

@ -98,24 +98,4 @@ export default function (route: Router, auth: Auth, storage: Storage): void {
}
}
);
route.post(
'/-/package/:package/dist-tags',
can('publish'),
async function (
req: $RequestExtend,
res: $ResponseExtend,
next: $NextFunctionVer
): Promise<void> {
try {
await storage.mergeTagsNext(req.params.package, req.body);
res.status(constants.HTTP_STATUS.CREATED);
return next({
ok: constants.API_MESSAGE.TAG_UPDATED,
});
} catch (err) {
next(err);
}
}
);
}

@ -1,88 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import buildDebug from 'debug';
import { Response } from 'express';
import _ from 'lodash';
import { HTTP_STATUS, USERS } from '@verdaccio/core';
import { Storage } from '@verdaccio/store';
import { $NextFunctionVer, $RequestExtend } from '../types/custom';
const debug = buildDebug('verdaccio:api:publish:star');
export default function (
storage: Storage
): (req: $RequestExtend, res: Response, next: $NextFunctionVer) => void {
const validateInputs = (newUsers, localUsers, username, isStar): boolean => {
const isExistlocalUsers = _.isNil(localUsers[username]) === false;
if (isStar && isExistlocalUsers && localUsers[username]) {
return true;
} else if (!isStar && isExistlocalUsers) {
return false;
} else if (!isStar && !isExistlocalUsers) {
return true;
}
return false;
};
return (req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
const name = req.params.package;
debug('starring a package for %o', name);
// const afterChangePackage = function (err?: Error) {
// if (err) {
// debug('error on update package for %o', name);
// return next(err);
// }
// debug('succes update package for %o', name);
// res.status(HTTP_STATUS.OK);
// next({
// success: true,
// });
// };
debug('get package info package for %o', name);
// @ts-ignore
// storage.getPackage({
// name,
// req,
// callback: function (err, info) {
// if (err) {
// debug('error on get package info package for %o', name);
// return next(err);
// }
// const newStarUser = req.body[USERS];
// const remoteUsername = req.remote_user.name;
// const localStarUsers = info[USERS];
// // Check is star or unstar
// const isStar = Object.keys(newStarUser).includes(remoteUsername);
// debug('is start? %o', isStar);
// if (
// _.isNil(localStarUsers) === false &&
// validateInputs(newStarUser, localStarUsers, remoteUsername, isStar)
// ) {
// return afterChangePackage();
// }
// const users = isStar
// ? {
// ...localStarUsers,
// [remoteUsername]: true,
// }
// : _.reduce(
// localStarUsers,
// (users, value, key) => {
// if (key !== remoteUsername) {
// users[key] = value;
// }
// return users;
// },
// {}
// );
// debug('update package for %o', name);
// storage.changePackage(name, { ...info, users }, req.body._rev, function (err) {
// afterChangePackage(err);
// });
// },
// });
};
}

@ -1,7 +1,7 @@
import { Response, Router } from 'express';
import _ from 'lodash';
import { HTTP_STATUS, USERS } from '@verdaccio/core';
import { HTTP_STATUS, USERS, errorUtils } from '@verdaccio/core';
import { Storage } from '@verdaccio/store';
import { Version } from '@verdaccio/types';
@ -11,13 +11,15 @@ export default function (route: Router, storage: Storage): void {
route.get(
'/-/_view/starredByUser',
async (req: $RequestExtend, res: Response, next: $NextFunctionVer): Promise<void> => {
const remoteUsername = req.remote_user.name;
const query: { key: string } = req.query;
if (typeof query?.key !== 'string') {
return next(errorUtils.getBadRequest('missing query key username'));
}
try {
const localPackages: Version[] = await storage.getLocalDatabaseNext();
const localPackages: Version[] = await storage.getLocalDatabase();
const filteredPackages: Version[] = localPackages.filter((localPackage: Version) =>
_.keys(localPackage[USERS]).includes(remoteUsername)
_.keys(localPackage[USERS]).includes(query?.key.toString().replace(/['"]+/g, ''))
);
res.status(HTTP_STATUS.OK);

@ -11,7 +11,7 @@ import {
generatePackageMetadata,
initializeServer as initializeServerHelper,
} from '@verdaccio/test-helper';
import { GenericBody } from '@verdaccio/types';
import { GenericBody, PackageUsers } from '@verdaccio/types';
import { buildToken, generateRandomHexString } from '@verdaccio/utils';
import apiMiddleware from '../../src';
@ -91,16 +91,54 @@ export function publishVersion(
app,
pkgName: string,
version: string,
distTags?: GenericBody
distTags?: GenericBody,
token?: string
): supertest.Test {
const pkgMetadata = generatePackageMetadata(pkgName, version, distTags);
return supertest(app)
const test = supertest(app)
.put(`/${encodeURIComponent(pkgName)}`)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.send(JSON.stringify(pkgMetadata))
.set('accept', HEADERS.GZIP)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
if (typeof token === 'string') {
test.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token));
}
return test;
}
export function starPackage(
app,
options: {
users: PackageUsers;
name: string;
_rev: string;
_id?: string;
},
token?: string
): supertest.Test {
const { _rev, _id, users } = options;
const starManifest = {
_rev,
_id,
users,
};
const test = supertest(app)
.put(`/${encodeURIComponent(options.name)}`)
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
.send(JSON.stringify(starManifest))
.set('accept', HEADERS.GZIP)
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
if (typeof token === 'string') {
test.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token));
}
return test;
}
export function getDisTags(app, pkgName) {

@ -0,0 +1,26 @@
auth:
htpasswd:
file: ./htpasswd-star
web:
enable: true
title: verdaccio
uplinks:
npmjs:
url: https://registry.npmjs.org/
log: { type: stdout, format: pretty, level: info }
packages:
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
_debug: true

@ -6,38 +6,8 @@ import { HTTP_STATUS } from '@verdaccio/core';
import { API_ERROR, API_MESSAGE, HEADERS, HEADER_TYPE } from '@verdaccio/core';
import { generatePackageMetadata, generateRemotePackageMetadata } from '@verdaccio/test-helper';
import { $RequestExtend, $ResponseExtend } from '../../types/custom';
import { getPackage, initializeServer, publishVersion } from './_helper';
const mockApiJWTmiddleware = jest.fn(
() =>
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
_next();
}
);
jest.mock('@verdaccio/auth', () => ({
Auth: class {
apiJWTmiddleware() {
return mockApiJWTmiddleware();
}
init() {
return Promise.resolve();
}
allow_access(_d, f_, cb) {
cb(null, true);
}
allow_publish(_d, f_, cb) {
cb(null, true);
}
allow_unpublish(_d, f_, cb) {
cb(null, true);
}
},
}));
describe('publish', () => {
describe('handle errors', () => {
const pkgName = 'test';
@ -80,6 +50,22 @@ describe('publish', () => {
});
});
});
test.each([['foo', '@scope/foo']])(
'should fails on publish a duplicated package',
async (pkgName) => {
const app = await initializeServer('publish.yaml');
await publishVersion(app, pkgName, '1.0.0');
return new Promise((resolve) => {
publishVersion(app, pkgName, '1.0.0')
.expect(HTTP_STATUS.CONFLICT)
.then((response) => {
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
resolve(response);
});
});
}
);
});
describe('publish a package', () => {
@ -141,6 +127,7 @@ describe('publish', () => {
});
});
});
describe('proxies setup', () => {
test.each([['foo', '@scope%2Ffoo']])(
'should publish a a patch package that already exist on a remote',
@ -172,22 +159,6 @@ describe('publish', () => {
});
});
test.each([['foo', '@scope/foo']])(
'should fails on publish a duplicated package',
async (pkgName) => {
const app = await initializeServer('publish.yaml');
await publishVersion(app, pkgName, '1.0.0');
return new Promise((resolve) => {
publishVersion(app, pkgName, '1.0.0')
.expect(HTTP_STATUS.CONFLICT)
.then((response) => {
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
resolve(response);
});
});
}
);
describe('unpublish a package', () => {
test.each([['foo', '@scope/foo']])('should unpublish entirely a package', async (pkgName) => {
const app = await initializeServer('publish.yaml');
@ -257,6 +228,4 @@ describe('publish', () => {
}
);
});
describe('star a package', () => {});
});

@ -0,0 +1,73 @@
import nock from 'nock';
import supertest from 'supertest';
import { HTTP_STATUS } from '@verdaccio/core';
import { HEADERS, HEADER_TYPE } from '@verdaccio/core';
import { getNewToken, getPackage, initializeServer, publishVersion, starPackage } from './_helper';
describe('star', () => {
test.each([['foo', '@scope%2Ffoo']])(
'should list stared packages for an user',
async (pkgName) => {
const userLogged = 'jota_token';
nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
const app = await initializeServer('star.yaml');
const token = await getNewToken(app, { name: userLogged, password: 'secretPass' });
await publishVersion(app, pkgName, '1.0.0', undefined, token).expect(HTTP_STATUS.CREATED);
await publishVersion(app, 'pkg-1', '1.0.0', undefined, token).expect(HTTP_STATUS.CREATED);
await publishVersion(app, 'pkg-2', '1.0.0', undefined, token).expect(HTTP_STATUS.CREATED);
const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
await starPackage(
app,
{
_rev: manifest.body._rev,
_id: manifest.body.id,
name: pkgName,
users: { [userLogged]: true },
},
token
).expect(HTTP_STATUS.CREATED);
await starPackage(
app,
{
_rev: manifest.body._rev,
_id: manifest.body.id,
name: 'pkg-1',
users: { [userLogged]: true },
},
token
).expect(HTTP_STATUS.CREATED);
await starPackage(
app,
{
_rev: manifest.body._rev,
_id: manifest.body.id,
name: 'pkg-2',
users: { [userLogged]: true },
},
token
).expect(HTTP_STATUS.CREATED);
const resp = await supertest(app)
.get(`/-/_view/starredByUser?key=%22jota_token%22`)
.set('Accept', HEADERS.JSON)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.OK);
expect(resp.body.rows).toHaveLength(3);
expect(resp.body.rows).toEqual([{ value: 'foo' }, { value: 'pkg-1' }, { value: 'pkg-2' }]);
}
);
test.each([['foo']])('should requires parameters', async (pkgName) => {
const userLogged = 'jota_token';
nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(404);
const app = await initializeServer('star.yaml');
const token = await getNewToken(app, { name: userLogged, password: 'secretPass' });
await publishVersion(app, pkgName, '1.0.0', undefined, token).expect(HTTP_STATUS.CREATED);
return supertest(app)
.get(`/-/_view/starredByUser?key_xxxxx=other`)
.set('Accept', HEADERS.JSON)
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(HTTP_STATUS.BAD_REQUEST);
});
});

@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express';
import { Logger, RemoteUser } from '@verdaccio/types';
export type $RequestExtend = Request & { remote_user?: any; log: Logger };
export type $RequestExtend = Request & { remote_user?: any; log: Logger; query?: { key: string } };
export type $ResponseExtend = Response & { cookies?: any };
export type $NextFunctionVer = NextFunction & any;

@ -589,7 +589,7 @@ class Storage {
}
}
public async getLocalDatabaseNext(): Promise<Version[]> {
public async getLocalDatabase(): Promise<Version[]> {
debug('get local database');
const storage = this.localStorage.getStoragePlugin();
const database = await storage.get();
@ -988,7 +988,7 @@ class Storage {
const localStarUsers = localPackage.users || {};
// if trying to add a star
const userIsAddingStar = Object.keys(users as PackageUsers).includes(username);
if (!isExecutingStarCommand(localPackage.users as PackageUsers, username, userIsAddingStar)) {
if (!isExecutingStarCommand(localPackage?.users as PackageUsers, username, userIsAddingStar)) {
return API_MESSAGE.PKG_CHANGED;
}
@ -1128,7 +1128,7 @@ class Storage {
// TODO: review why do this
versions[versionToPublish].readme =
_.isNil(manifest.readme) === false ? String(manifest.readme) : '';
await this.addVersionNext(name, versionToPublish, versions[versionToPublish], null);
await this.addVersion(name, versionToPublish, versions[versionToPublish], null);
} catch (err: any) {
logger.error({ err: err.message }, 'updated version has failed: @{err}');
debug('error on create a version for %o with error %o', name, err.message);
@ -1139,7 +1139,7 @@ class Storage {
// 2. update and merge tags
let mergedManifest;
try {
// note: I could merge this with addVersionNext
// note: I could merge this with addVersion()
// 1. add version
// 2. merge versions
// 3. upload tarball
@ -1325,7 +1325,7 @@ class Storage {
* @param metadata version metadata
* @param tag tag of the version
*/
public async addVersionNext(
private async addVersion(
name: string,
version: string,
metadata: Version,

@ -1090,7 +1090,7 @@ describe('storage', () => {
);
const storage = new Storage(config);
await storage.init(config);
await expect(storage.getLocalDatabaseNext()).resolves.toHaveLength(0);
await expect(storage.getLocalDatabase()).resolves.toHaveLength(0);
});
test('should return 1 local packages', async () => {
@ -1122,7 +1122,7 @@ describe('storage', () => {
host: req.get('host') as string,
},
});
const response = await storage.getLocalDatabaseNext();
const response = await storage.getLocalDatabase();
expect(response).toHaveLength(1);
expect(response[0]).toEqual(expect.objectContaining({ name: 'foo', version: '1.0.0' }));
});

@ -97,7 +97,7 @@ function addPackageWebApi(storage: Storage, auth: Auth, config: Config): Router
debug('hit package web api %o');
try {
const localPackages: Version[] = await storage.getLocalDatabaseNext();
const localPackages: Version[] = await storage.getLocalDatabase();
const order = getOrder(config?.web?.sort_packages);
debug('order %o', order);