fix: build

This commit is contained in:
2023-03-14 18:04:29 +01:00
parent 3792a14e90
commit fd740b3091
185 changed files with 21932 additions and 71486 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.idea
.vscode
.turbo
node_modules
.env.local

28
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,28 @@
image: node:18-buster
before_script:
- echo TURBO_API=$TURBO_API >> .env.local
- echo TURBO_TOKEN=$TURBO_TOKEN >> .env.local
- echo TURBO_TEAM=$TURBO_TEAM >> .env.local
stages:
- build
build:
stage: build
before_script:
- corepack enable
- corepack prepare pnpm@latest-7 --activate
- pnpm config set store-dir .pnpm-store
script:
- pnpm install
- pnpm run syncpack
- pnpm lint
- pnpm format
- pnpm build
cache:
key:
files:
- pnpm-lock.yaml
paths:
- .pnpm-store

View File

@@ -0,0 +1,5 @@
#!/bin/bash
echo TURBO_API=$TURBO_API >> .env.local
echo TURBO_TOKEN=$TURBO_TOKEN >> .env.local
echo TURBO_TEAM=$TURBO_TEAM >> .env.local

View File

@@ -0,0 +1,32 @@
## Summary
(Summarize the bug encountered concisely)
## Steps to reproduce
(How one can reproduce the issue - this is very important)
## Example Project
(If possible, please create an example project here on GitLab.com that exhibits the problematic behaviour, and link to it here in the bug report)
(If you are using an older version of GitLab, this will also determine whether the bug has been fixed in a more recent version)
## What is the current bug behavior?
(What actually happens)
## What is the expected correct behavior?
(What you should see instead)
## Relevant logs and/or screenshots
(Paste any relevant logs - please use code blocks (```) to format console output,
logs, and code as it's very hard to read otherwise.)
## Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
/label ~bug ~meeting

View File

@@ -0,0 +1,17 @@
## Description
(Describe the feature that you're requesting concisely)
## Explanation
(Explain why the feature is necessary)
## Mockups/Screenshots
(If possible, provide mockups or screenshots, which demonstrate the feature)
## Dependencies, issues to be resolved beforehand
(List issues or dependencies that need to be resolved before this feature can be implemented)
/label ~feature ~meeting

View File

@@ -0,0 +1,17 @@
## What needs to be changed?
??? - Describe use case!
## How is the current state not sufficient?
???
## Which changes are necessary?
???
## Do the proposed changes impact current use cases?
???
/label ~improvement ~meeting

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
auto-install-peers=true

90
.syncpackrc.json Normal file
View File

@@ -0,0 +1,90 @@
{
"semverRange": "",
"source": [
"package.json",
"**/package.json"
],
"indent": " ",
"sortFirst": [
"name",
"description",
"version",
"type",
"license",
"repository",
"author",
"contributors",
"prettier",
"main",
"types",
"bin",
"scripts",
"dependencies",
"devDependencies",
"peerDependencies"
],
"versionGroups": [
{
"label": "ES Mapping Generator Special Dependencies",
"dependencies": ["typescript", "typedoc", "ts-node"],
"packages": ["@openstapps/es-mapping-generator"],
"isIgnored": true
},
{
"label": "Should have the same version",
"dependencies": ["**"],
"dependencyTypes": ["workspace"],
"packages": ["**"],
"pinVersion": "2.1.0"
},
{
"label": "Packages should use workspace version",
"dependencies": ["@openstapps/**"],
"dependencyTypes": ["prod", "dev"],
"packages": ["**"],
"pinVersion": "workspace:*"
},
{
"label": "Packages should be synced to configuration",
"dependencies": [
"typescript",
"ts-node",
"eslint",
"prettier"
],
"packages": [
"**"
],
"snapTo": [
"@openstapps/configuration",
"@openstapps/prettier-config",
"@openstapps/eslint-config",
"@openstapps/app"
]
},
{
"label": "No Banned Dependencies",
"dependencies": [
"tslint",
"lodash",
"@types/lodash"
],
"packages": [
"**"
],
"isBanned": true
},
{
"label": "No @types package in prod dependencies of applications",
"dependencies": ["@types/**"],
"dependencyTypes": ["prod"],
"packages": [
"@openstapps/app",
"@openstapps/backend",
"@openstapps/proxy",
"@openstapps/minimal**"
],
"isBanned": true
}
]
}

38
README.md Normal file
View File

@@ -0,0 +1,38 @@
# Open StApps Monorepo
## Remote caching
Turbo supports remote caching, which massively speeds up build processes.
### Connecting to the remote cache locally
`.env.local`
```dotenv
TURBO_API=http://example:3000
TURBO_TEAM=openstapps
TURBO_TOKEN=abc123
```
### Connecting to the remote cache in GitLab Pipelines
You will need to define
* `TURBO_API`
* `TURBO_TEAM`
* `TURBO_TOKEN`
Like you did locally as described in [this](https://turbo.build/repo/docs/ci/gitlabci#remote-caching)
guide.
### Hosting a cache
Self-hosting via Docker is extremely simple, just follow
[this](http://v2202207178592194230.supersrv.de:6341) guide
or in short:
`.env`
```dotenv
PORT=...
TURBO_TOKEN=...
```
```shell
docker run --env-file=.env -p 3000:3000 fox1t/turborepo-remote-cache
```

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/backend", "name": "@openstapps/backend",
"version": "1.0.0",
"description": "A reference implementation for a StApps backend", "description": "A reference implementation for a StApps backend",
"version": "2.1.0",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"author": "André Bierlein <andre.mt.bierlein@gmail.com>", "author": "André Bierlein <andre.mt.bierlein@gmail.com>",
"contributors": [ "contributors": [
@@ -18,9 +18,10 @@
"build": "npm run lint && npm run compile", "build": "npm run lint && npm run compile",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
"check-configuration": "openstapps-configuration", "check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'", "compile": "rimraf lib && tsc",
"documentation": "typedoc --includeVersion --out docs --readme README.md --listInvalidSymbolLinks --entryPointStrategy expand src && openstapps-core-tools openapi ./node_modules/@openstapps/core/lib ./docs/openapi && openapi build-docs docs/openapi/openapi.json -o docs/openapi/index.html", "documentation": "typedoc --includeVersion --out docs --readme README.md --listInvalidSymbolLinks --entryPointStrategy expand src && openstapps-core-tools openapi ./node_modules/@openstapps/core/lib ./docs/openapi && openapi build-docs docs/openapi/openapi.json -o docs/openapi/index.html",
"version": "npm run changelog", "lint": "eslint -c .eslintrc.json --ignore-path .eslintignore --ext .ts src/ test/",
"lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts src/ test/",
"prepublishOnly": "npm ci && npm run build", "prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly", "preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"", "push": "git push && git push origin \"v$npm_package_version\"",
@@ -29,14 +30,14 @@
"test": "npm run test-unit && npm run test-integration", "test": "npm run test-unit && npm run test-integration",
"test-unit": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 nyc mocha --require ts-node/register --exit 'test/**/*.spec.ts'", "test-unit": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 nyc mocha --require ts-node/register --exit 'test/**/*.spec.ts'",
"test-integration": "docker-compose -f integration-test.yml pull && docker-compose -f integration-test.yml up --build --abort-on-container-exit --exit-code-from apicli", "test-integration": "docker-compose -f integration-test.yml pull && docker-compose -f integration-test.yml up --build --abort-on-container-exit --exit-code-from apicli",
"lint": "eslint -c .eslintrc.json --ignore-path .eslintignore --ext .ts src/ test/", "test-unit": "env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 nyc mocha --require ts-node/register --exit 'test/**/*.spec.ts'",
"lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts src/ test/" "version": "npm run changelog"
}, },
"dependencies": { "dependencies": {
"@elastic/elasticsearch": "8.4.0", "@elastic/elasticsearch": "8.4.0",
"@openstapps/core": "1.0.1", "@openstapps/core": "workspace:*",
"@openstapps/core-tools": "0.34.0", "@openstapps/core-tools": "workspace:*",
"@openstapps/logger": "1.1.1", "@openstapps/logger": "workspace:*",
"@redocly/cli": "1.0.0-beta.125", "@redocly/cli": "1.0.0-beta.125",
"@types/node": "14.18.43", "@types/node": "14.18.43",
"config": "3.3.9", "config": "3.3.9",
@@ -44,7 +45,7 @@
"express": "4.18.2", "express": "4.18.2",
"express-prom-bundle": "6.6.0", "express-prom-bundle": "6.6.0",
"express-promise-router": "4.1.1", "express-promise-router": "4.1.1",
"got": "11.8.6", "got": "12.6.0",
"moment": "2.29.4", "moment": "2.29.4",
"morgan": "1.10.0", "morgan": "1.10.0",
"nock": "13.3.1", "nock": "13.3.1",
@@ -57,9 +58,9 @@
"uuid": "8.3.2" "uuid": "8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.34.0", "@openstapps/configuration": "workspace:*",
"@openstapps/es-mapping-generator": "0.6.0", "@openstapps/es-mapping-generator": "workspace:*",
"@openstapps/eslint-config": "1.1.0", "@openstapps/eslint-config": "workspace:*",
"@testdeck/mocha": "0.3.3", "@testdeck/mocha": "0.3.3",
"@types/chai": "4.3.4", "@types/chai": "4.3.4",
"@types/chai-as-promised": "7.1.5", "@types/chai-as-promised": "7.1.5",
@@ -69,14 +70,15 @@
"@types/geojson": "1.0.6", "@types/geojson": "1.0.6",
"@types/mocha": "10.0.1", "@types/mocha": "10.0.1",
"@types/morgan": "1.9.4", "@types/morgan": "1.9.4",
"@types/node": "18.15.3",
"@types/node-cron": "3.0.7", "@types/node-cron": "3.0.7",
"@types/nodemailer": "6.4.7", "@types/nodemailer": "6.4.7",
"@types/promise-queue": "2.2.0", "@types/promise-queue": "2.2.0",
"@types/sinon-express-mock": "1.3.9", "@types/sinon-express-mock": "1.3.9",
"@types/supertest": "2.0.12", "@types/supertest": "2.0.12",
"@types/uuid": "8.3.4", "@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.42.0", "@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.42.0", "@typescript-eslint/parser": "5.49.0",
"chai": "4.3.7", "chai": "4.3.7",
"chai-as-promised": "7.1.1", "chai-as-promised": "7.1.1",
"conventional-changelog-cli": "2.2.2", "conventional-changelog-cli": "2.2.2",
@@ -85,14 +87,13 @@
"eslint-config-prettier": "8.8.0", "eslint-config-prettier": "8.8.0",
"eslint-plugin-jsdoc": "39.9.1", "eslint-plugin-jsdoc": "39.9.1",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "43.0.2", "eslint-plugin-unicorn": "45.0.2",
"get-port": "5.1.1", "get-port": "5.1.1",
"mocha": "10.2.0", "mocha": "10.2.0",
"mocked-env": "1.3.5", "mocked-env": "1.3.5",
"nyc": "15.1.0", "nyc": "15.1.0",
"prepend-file-cli": "1.0.6",
"prettier": "2.8.8", "prettier": "2.8.8",
"rimraf": "3.0.2", "rimraf": "4.4.0",
"sinon": "14.0.2", "sinon": "14.0.2",
"sinon-express-mock": "2.2.1", "sinon-express-mock": "2.2.1",
"supertest": "6.3.3", "supertest": "6.3.3",

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/* /*
* Copyright (C) 2019 StApps * Copyright (C) 2019 StApps
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@@ -68,16 +70,19 @@ async function onError(error: {code: string; syscall: string}) {
// handle specific listen errors with friendly messages // handle specific listen errors with friendly messages
switch (error.code) { switch (error.code) {
case 'EACCES': case 'EACCES': {
await Logger.error(`${bind} requires elevated privileges`); await Logger.error(`${bind} requires elevated privileges`);
process.exit(1); process.exit(1);
break; break;
case 'EADDRINUSE': }
case 'EADDRINUSE': {
await Logger.error(`${bind} is already in use`); await Logger.error(`${bind} is already in use`);
process.exit(1); process.exit(1);
break; break;
default: }
default: {
throw error; throw error;
}
} }
} }
@@ -87,11 +92,11 @@ async function onError(error: {code: string; syscall: string}) {
function onListening() { function onListening() {
const addr = server.address(); const addr = server.address();
if (addr !== null) { if (addr === null) {
void Logger.error(`Failed to start binding`);
} else {
const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`;
Logger.ok(`Listening on ${bind}`); Logger.ok(`Listening on ${bind}`);
} else {
void Logger.error(`Failed to start binding`);
} }
} }

View File

@@ -83,18 +83,18 @@ export class MailQueue {
throw new Error('Failed to initialize the SMTP transport for the mail queue'); throw new Error('Failed to initialize the SMTP transport for the mail queue');
} }
if (!this.transport.isVerified()) { if (this.transport.isVerified()) {
this.verificationCounter++;
setTimeout(() => {
Logger.warn('Transport not verified yet. Trying to send mails here...');
this.checkForVerification();
}, MailQueue.VERIFICATION_TIMEOUT);
} else {
Logger.ok('Transport for mail queue was verified. We can send mails now'); Logger.ok('Transport for mail queue was verified. We can send mails now');
// if the transport finally was verified send all our mails from the dry queue // if the transport finally was verified send all our mails from the dry queue
for (const mail of this.dryQueue) { for (const mail of this.dryQueue) {
void this.addToQueue(mail); void this.addToQueue(mail);
} }
} else {
this.verificationCounter++;
setTimeout(() => {
Logger.warn('Transport not verified yet. Trying to send mails here...');
this.checkForVerification();
}, MailQueue.VERIFICATION_TIMEOUT);
} }
} }
@@ -104,12 +104,12 @@ export class MailQueue {
* @param mail Information required for sending a mail * @param mail Information required for sending a mail
*/ */
public async push(mail: MailOptions) { public async push(mail: MailOptions) {
if (!this.transport.isVerified()) { if (this.transport.isVerified()) {
await this.addToQueue(mail);
} else {
// the transport has verification, but is not verified yet // the transport has verification, but is not verified yet
// push to a dry queue which gets pushed to the real queue when the transport is verified // push to a dry queue which gets pushed to the real queue when the transport is verified
this.dryQueue.push(mail); this.dryQueue.push(mail);
} else {
await this.addToQueue(mail);
} }
} }
} }

View File

@@ -47,10 +47,12 @@ export async function pluginRegisterHandler(
_app: Express.Application, _app: Express.Application,
): Promise<SCPluginRegisterResponse> { ): Promise<SCPluginRegisterResponse> {
switch (request.action) { switch (request.action) {
case 'add': case 'add': {
return addPlugin(request.plugin); return addPlugin(request.plugin);
case 'remove': }
case 'remove': {
return removePlugin(request.route); return removePlugin(request.route);
}
} }
} }

View File

@@ -114,17 +114,21 @@ export async function setUp(
// make a schedule for each trigger // make a schedule for each trigger
for (const trigger of watcher.triggers) { for (const trigger of watcher.triggers) {
switch (trigger.executionTime) { switch (trigger.executionTime) {
case 'hourly': case 'hourly': {
trigger.executionTime = '5 * * * *'; trigger.executionTime = '5 * * * *';
break; break;
case 'daily': }
case 'daily': {
trigger.executionTime = '5 0 * * *'; trigger.executionTime = '5 0 * * *';
break; break;
case 'weekly': }
case 'weekly': {
trigger.executionTime = '5 0 * * 0'; trigger.executionTime = '5 0 * * 0';
break; break;
case 'monthly': }
case 'monthly': {
trigger.executionTime = '5 0 1 * *'; trigger.executionTime = '5 0 1 * *';
}
} }
cron.schedule(trigger.executionTime, async () => { cron.schedule(trigger.executionTime, async () => {

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,34 @@
{ {
"name": "@openstapps/proxy", "name": "@openstapps/proxy",
"version": "1.5.1",
"description": "NGINX proxy that is dynamically configured by a Node.js script", "description": "NGINX proxy that is dynamically configured by a Node.js script",
"version": "2.1.0",
"license": "AGPL-3.0-only",
"repository": "git@gitlab.com:openstapps/proxy.git",
"author": "Anselm Rochus Stordeur <anselmstordeur@gmail.com>",
"contributors": [
"André Bierlein <andre.mt.bierlein@gmail.com>",
"Benjamin Joeckel",
"Jovan Krunic <jovan.krunic@gmail.com>",
"Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"Michel Jonathan Schmitz <michel.schmitz_1992@hotmail.com>",
"Rainer Killinger <mail-openstapps@killinger.co>"
],
"main": "./lib/cli.js", "main": "./lib/cli.js",
"scripts": {
"build": "npm run lint && npm run compile",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc",
"documentation": "typedoc --includeVersion --out docs --readme README.md --entryPointStrategy expand src",
"lint": "eslint --ext .ts src/",
"postversion": "npm run changelog",
"prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "nyc mocha --require ts-node/register 'test/**/*.spec.ts'"
},
"dependencies": { "dependencies": {
"@openstapps/logger": "1.1.1", "@openstapps/logger": "workspace:*",
"@types/config": "3.3.0", "@types/config": "3.3.0",
"@types/dockerode": "3.3.17", "@types/dockerode": "3.3.17",
"@types/node": "14.18.36", "@types/node": "14.18.36",
@@ -17,66 +41,37 @@
"semver": "7.3.8" "semver": "7.3.8"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.34.0", "@openstapps/configuration": "workspace:*",
"@openstapps/eslint-config": "1.1.0", "@openstapps/eslint-config": "workspace:*",
"@testdeck/mocha": "0.3.3", "@testdeck/mocha": "0.3.3",
"@types/chai": "4.3.5", "@types/chai": "4.3.5",
"@types/chai-spies": "1.0.3", "@types/chai-spies": "1.0.3",
"@types/config": "3.3.0",
"@types/dockerode": "3.3.14",
"@types/mustache": "4.2.2", "@types/mustache": "4.2.2",
"@types/node": "18.15.3",
"@types/proxyquire": "1.3.28", "@types/proxyquire": "1.3.28",
"@typescript-eslint/eslint-plugin": "5.42.1", "@types/semver": "7.3.13",
"@typescript-eslint/parser": "5.42.1", "@types/sha1": "1.1.3",
"@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.49.0",
"chai": "4.3.7", "chai": "4.3.7",
"chai-spies": "1.0.0", "chai-spies": "1.0.0",
"conventional-changelog-cli": "2.2.2", "conventional-changelog-cli": "2.2.2",
"eslint": "8.31.0", "eslint": "8.33.0",
"eslint-config-prettier": "8.6.0", "eslint-config-prettier": "8.6.0",
"eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-jsdoc": "39.7.4",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "44.0.2", "eslint-plugin-unicorn": "45.0.2",
"mocha": "10.2.0", "mocha": "10.2.0",
"nyc": "15.1.0", "nyc": "15.1.0",
"prepend-file-cli": "1.0.6", "prettier": "2.8.3",
"prettier": "2.8.2",
"proxyquire": "2.1.3", "proxyquire": "2.1.3",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"typedoc": "0.22.18", "typedoc": "0.22.18",
"typescript": "4.4.4" "typescript": "4.4.4"
}, },
"scripts": {
"build": "npm run lint && npm run compile",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'",
"documentation": "typedoc --includeVersion --out docs --readme README.md --entryPointStrategy expand src",
"lint": "eslint --ext .ts src/",
"postversion": "npm run changelog",
"prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "nyc mocha --require ts-node/register 'test/**/*.spec.ts'"
},
"repository": {
"type": "git",
"url": "git@gitlab.com:openstapps/proxy.git"
},
"author": "Anselm Rochus Stordeur <anselmstordeur@gmail.com>",
"contributors": [
"André Bierlein <andre.mt.bierlein@gmail.com>",
"Benjamin Joeckel",
"Jovan Krunic <jovan.krunic@gmail.com>",
"Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"Michel Jonathan Schmitz <michel.schmitz_1992@hotmail.com>",
"Rainer Killinger <mail-openstapps@killinger.co>"
],
"license": "AGPL-3.0-only",
"openstappsConfiguration": {
"forPackaging": false,
"ignoreCiEntries": [
"package"
]
},
"nyc": { "nyc": {
"all": true, "all": true,
"branches": 95, "branches": 95,
@@ -99,5 +94,11 @@
"text-summary" "text-summary"
], ],
"statements": 95 "statements": 95
},
"openstappsConfiguration": {
"forPackaging": false,
"ignoreCiEntries": [
"package"
]
} }
} }

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify

View File

@@ -192,7 +192,6 @@ export async function generateUpstreamMap(
}) })
.join('\n'); .join('\n');
// eslint-disable-next-line prettier/prettier
result += '\n\}'; result += '\n\}';
if (!foundMatchingContainer) { if (!foundMatchingContainer) {

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./lib/cli.js')

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,9 @@
{ {
"name": "@openstapps/configuration", "name": "@openstapps/configuration",
"version": "0.34.0",
"description": "A collection of configuration base files for StApps projects.", "description": "A collection of configuration base files for StApps projects.",
"scripts": { "version": "2.1.0",
"build": "npm run lint && npm run compile", "license": "GPL-3.0-only",
"compile": "rimraf lib && tsc --outDir lib && prepend lib/cli.js '#!/usr/bin/env node\n'", "repository": "git@gitlab.com:openstapps/configuration.git",
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
"documentation": "typedoc --out docs --readme README.md --includeVersion --listInvalidSymbolLinks --entryPointStrategy expand src",
"prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint -c .eslintrc.json --ignore-path .eslintignore --ext .ts src/"
},
"repository": {
"type": "git",
"url": "git@gitlab.com:openstapps/configuration.git"
},
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>", "author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"contributors": [ "contributors": [
"Anselm Rochus Stordeur", "Anselm Rochus Stordeur",
@@ -24,36 +11,47 @@
"Rainer Killinger <mail-openstapps@killinger.co>", "Rainer Killinger <mail-openstapps@killinger.co>",
"Thea Schöbl" "Thea Schöbl"
], ],
"license": "GPL-3.0-only", "prettier": "@openstapps/prettier-config",
"bin": {
"openstapps-configuration": "app.js"
},
"scripts": {
"build": "npm run lint && npm run compile",
"compile": "rimraf lib && tsc --outDir lib",
"documentation": "typedoc --out docs --readme README.md --includeVersion --listInvalidSymbolLinks --entryPointStrategy expand src",
"format:fix": "prettier --write .",
"lint": "eslint -c .eslintrc.json --ignore-path .eslintignore --ext .ts src/",
"prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "echo \"Error: no test specified\" && exit 1",
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md"
},
"dependencies": { "dependencies": {
"@types/node": "14.18.36",
"@types/semver": "7.3.13",
"@types/yaml": "1.9.7",
"chalk": "4.1.2", "chalk": "4.1.2",
"commander": "9.5.0", "commander": "10.0.0",
"semver": "7.3.8", "semver": "7.3.8",
"yaml": "1.10.2" "yaml": "1.10.2"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/eslint-config": "1.1.0", "@openstapps/eslint-config": "workspace:*",
"@typescript-eslint/eslint-plugin": "5.33.1", "@openstapps/prettier-config": "workspace:*",
"@typescript-eslint/parser": "5.33.1", "@types/node": "18.15.3",
"@types/semver": "7.3.13",
"@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.49.0",
"conventional-changelog-cli": "2.2.2", "conventional-changelog-cli": "2.2.2",
"eslint": "8.31.0", "eslint": "8.33.0",
"eslint-config-prettier": "8.6.0", "eslint-config-prettier": "8.6.0",
"eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-jsdoc": "39.7.4",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "43.0.2", "eslint-plugin-unicorn": "45.0.2",
"prepend-file-cli": "1.0.6", "prettier": "2.8.3",
"prettier": "2.8.2", "rimraf": "4.4.0",
"rimraf": "3.0.2",
"typedoc": "0.22.18", "typedoc": "0.22.18",
"typescript": "4.4.4" "typescript": "4.4.4"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "4.4.4" "typescript": "4.4.4"
},
"bin": {
"openstapps-configuration": "lib/cli.js"
} }
} }

View File

@@ -819,11 +819,6 @@ export function getRules(configuration: Configuration): Rules {
scripts.build = 'npm run lint && npm run compile'; scripts.build = 'npm run lint && npm run compile';
scripts.compile = 'rimraf lib && tsc'; scripts.compile = 'rimraf lib && tsc';
devDependencies.push('rimraf'); devDependencies.push('rimraf');
if (configuration.hasCli) {
devDependencies.push('prepend-file-cli');
scripts.compile += " && prepend lib/cli.js '#!/usr/bin/env node\n'";
}
} }
if (configuration.standardDocumentation) { if (configuration.standardDocumentation) {

View File

@@ -6,6 +6,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"inlineSourceMap": true, "inlineSourceMap": true,
"module": "CommonJS", "module": "CommonJS",
"moduleResolution": "node", "moduleResolution": "node",
@@ -16,6 +17,7 @@
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"outDir": "../../../lib/", "outDir": "../../../lib/",
"lib": ["ES2021", "DOM"],
"strict": true, "strict": true,
"target": "es6" "target": "es6"
}, },

View File

@@ -13,12 +13,11 @@ module.exports = {
}, },
extends: [ extends: [
'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:prettier/recommended',
'plugin:jsdoc/recommended', 'plugin:jsdoc/recommended',
'plugin:unicorn/recommended', 'plugin:unicorn/recommended',
'prettier'
], ],
plugins: ['eslint-plugin-unicorn', 'eslint-plugin-jsdoc', 'prettier'], plugins: ['eslint-plugin-unicorn', 'eslint-plugin-jsdoc'],
settings: { settings: {
jsdoc: { jsdoc: {
mode: 'typescript', mode: 'typescript',
@@ -43,6 +42,9 @@ module.exports = {
], ],
'unicorn/no-nested-ternary': 'off', 'unicorn/no-nested-ternary': 'off',
'unicorn/better-regex': 'off', 'unicorn/better-regex': 'off',
'unicorn/no-typeof-undefined': 'off',
'unicorn/no-non-null-assertion': 'off',
'unicorn/prefer-array-some': 'off',
'jsdoc/no-types': 'error', 'jsdoc/no-types': 'error',
'jsdoc/require-param': 'off', 'jsdoc/require-param': 'off',
@@ -82,22 +84,6 @@ module.exports = {
'@typescript-eslint/lines-between-class-members': ['error', 'always'], '@typescript-eslint/lines-between-class-members': ['error', 'always'],
'@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-non-null-assertion': 'off',
'prettier/prettier': [
'error',
{
tabWidth: 2,
printWidth: 110,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'consistent',
trailingComma: 'all',
bracketSpacing: false,
arrowParens: 'avoid',
endOfLine: 'lf',
},
],
}, },
}, },
], ],

File diff suppressed because it is too large Load Diff

View File

@@ -1,47 +1,42 @@
{ {
"name": "@openstapps/eslint-config", "name": "@openstapps/eslint-config",
"version": "1.1.0",
"main": "index.js",
"description": "A collection of configuration base files for StApps projects.", "description": "A collection of configuration base files for StApps projects.",
"scripts": { "version": "2.1.0",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'", "license": "GPL-3.0-only",
"test": "eslint -c index.js test/test-file.ts", "repository": "git@gitlab.com:openstapps/eslint-config.git",
"prepublishOnly": "npm ci && npm test",
"postversion": "npm run changelog",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\""
},
"repository": {
"type": "git",
"url": "git@gitlab.com:openstapps/eslint-config.git"
},
"author": "Thea Schöbl", "author": "Thea Schöbl",
"contributors": [ "contributors": [
"Rainer Killinger <mail-openstapps@killinger.co>" "Rainer Killinger <mail-openstapps@killinger.co>"
], ],
"license": "GPL-3.0-only", "main": "index.js",
"dependencies": {}, "scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"postversion": "npm run changelog",
"prepublishOnly": "npm ci && npm test",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "eslint -c index.js test/test-file.ts"
},
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.31.0", "@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/eslint-plugin": ">=5.29.0", "@typescript-eslint/parser": "5.49.0",
"@typescript-eslint/parser": ">=5.29.0",
"conventional-changelog-cli": "2.2.2", "conventional-changelog-cli": "2.2.2",
"eslint": ">=8.18.0", "eslint": "8.33.0",
"eslint-config-prettier": ">=8.5.0", "eslint-config-prettier": "8.6.0",
"eslint-plugin-jsdoc": ">=39.3.3", "eslint-plugin-jsdoc": "39.7.4",
"eslint-plugin-prettier": ">=4.0.0", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": ">=42.0.0", "eslint-plugin-unicorn": "45.0.2",
"prettier": ">=2.7.1", "prettier": "2.8.3",
"typescript": "4.4.4" "typescript": "4.4.4"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/eslint-plugin": ">=5.29.0", "@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": ">=5.29.0", "@typescript-eslint/parser": "5.49.0",
"eslint": ">=8.18.0", "eslint": "8.33.0",
"eslint-config-prettier": ">=8.5.0", "eslint-config-prettier": "8.6.0",
"eslint-plugin-jsdoc": ">=39.3.3", "eslint-plugin-jsdoc": "39.7.4",
"eslint-plugin-prettier": ">=4.0.0", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": ">=41.0.1", "eslint-plugin-unicorn": "45.0.2",
"prettier": ">=2.7.1" "prettier": "2.8.3"
} }
} }

View File

@@ -1,12 +0,0 @@
/**
* Simple TS source file to get linted
* Can be used to verify new/changed rule behavior
*/
function addNumbers(a: number, b: number) {
return a + b;
}
const sum: number = addNumbers(10, 15);
// eslint-disable-next-line no-console
console.log('Sum of the two numbers is: ' + sum);

View File

@@ -1,23 +1,20 @@
{ {
"name": "@openstapps/prettier-config", "name": "@openstapps/prettier-config",
"version": "1.0.0",
"description": "StApps Prettier Config", "description": "StApps Prettier Config",
"main": "index.json", "version": "2.1.0",
"author": "Thea Schöbl <dev@theaninova.de>",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/prettier-config.git",
"author": "Thea Schöbl <dev@theaninova.de>",
"contributors": [ "contributors": [
"Rainer Killinger <mail-openstapps@killinger.co>" "Rainer Killinger <mail-openstapps@killinger.co>"
], ],
"repository": { "main": "index.json",
"type": "git",
"url": "git@gitlab.com:openstapps/prettier-config.git"
},
"scripts": { "scripts": {
"changelog": "npx conventional-changelog-cli -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'", "changelog": "npx conventional-changelog-cli -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"test": "npx prettier --config index.json --check \"test/*.js\"",
"prepublishOnly": "npm test",
"postversion": "npm run changelog", "postversion": "npm run changelog",
"prepublishOnly": "npm test",
"preversion": "npm run prepublishOnly", "preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"" "push": "git push && git push origin \"v$npm_package_version\"",
"test": "npx prettier --config index.json --check \"test/*.js\""
} }
} }

View File

@@ -116,7 +116,7 @@ remind:
stage: maintenance stage: maintenance
only: only:
variables: variables:
- $MAINTENANCE_MODE == "remind" - $MAINTENANCE_MODE == "remind"
tags: tags:
- secrecy - secrecy
@@ -173,7 +173,7 @@ renovate:
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
DOCKER_TLS_CERTDIR: /certs DOCKER_TLS_CERTDIR: /certs
DOCKER_CERT_PATH: /certs/client DOCKER_CERT_PATH: /certs/client
RENOVATE_EXTENDS: "gitlab>openstapps/projectmanagement" RENOVATE_EXTENDS: 'gitlab>openstapps/projectmanagement'
RENOVATE_BASE_DIR: $CI_PROJECT_DIR/renovate RENOVATE_BASE_DIR: $CI_PROJECT_DIR/renovate
RENOVATE_ENDPOINT: $CI_API_V4_URL RENOVATE_ENDPOINT: $CI_API_V4_URL
RENOVATE_PLATFORM: gitlab RENOVATE_PLATFORM: gitlab
@@ -195,7 +195,7 @@ renovate:
when: always when: always
expire_in: 1d expire_in: 1d
paths: paths:
- "$RENOVATE_LOG_FILE" - '$RENOVATE_LOG_FILE'
before_script: [] before_script: []
script: script:
- renovate $RENOVATE_EXTRA_FLAGS - renovate $RENOVATE_EXTRA_FLAGS

View File

@@ -2,35 +2,29 @@
(Summarize the bug encountered concisely) (Summarize the bug encountered concisely)
## Steps to reproduce ## Steps to reproduce
(How one can reproduce the issue - this is very important) (How one can reproduce the issue - this is very important)
## Example Project ## Example Project
(If possible, please create an example project here on GitLab.com that exhibits the problematic behaviour, and link to it here in the bug report) (If possible, please create an example project here on GitLab.com that exhibits the problematic behaviour, and link to it here in the bug report)
(If you are using an older version of GitLab, this will also determine whether the bug has been fixed in a more recent version) (If you are using an older version of GitLab, this will also determine whether the bug has been fixed in a more recent version)
## What is the current bug behavior? ## What is the current bug behavior?
(What actually happens) (What actually happens)
## What is the expected correct behavior? ## What is the expected correct behavior?
(What you should see instead) (What you should see instead)
## Relevant logs and/or screenshots ## Relevant logs and/or screenshots
(Paste any relevant logs - please use code blocks (```) to format console output, (Paste any relevant logs - please use code blocks (```) to format console output,
logs, and code as it's very hard to read otherwise.) logs, and code as it's very hard to read otherwise.)
## Possible fixes ## Possible fixes
(If you can, link to the line of code that might be responsible for the problem) (If you can, link to the line of code that might be responsible for the problem)

View File

@@ -2,15 +2,12 @@
(Describe the feature that you're requesting concisely) (Describe the feature that you're requesting concisely)
## Explanation ## Explanation
(Explain why the feature is necessary) (Explain why the feature is necessary)
## Dependencies, issues to be resolved beforehand ## Dependencies, issues to be resolved beforehand
(List issues or dependencies that need to be resolved before this feature can be implemented) (List issues or dependencies that need to be resolved before this feature can be implemented)
/label ~meeting /label ~meeting

View File

@@ -1,314 +1,206 @@
# [0.25.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.24.0...v0.25.0) (2023-01-17) # [0.25.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.24.0...v0.25.0) (2023-01-17)
### Bug Fixes ### Bug Fixes
* broken cypress build ([a37d3a9](https://gitlab.com/openstapps/projectmanagement/commit/a37d3a9ec4c8303bef7f7f29fdb8f7401ef7b02a)) - broken cypress build ([a37d3a9](https://gitlab.com/openstapps/projectmanagement/commit/a37d3a9ec4c8303bef7f7f29fdb8f7401ef7b02a))
* renovate job timeout ([116c880](https://gitlab.com/openstapps/projectmanagement/commit/116c8809e4f7d524361275e910fdc582a9baf78a)) - renovate job timeout ([116c880](https://gitlab.com/openstapps/projectmanagement/commit/116c8809e4f7d524361275e910fdc582a9baf78a))
# [0.24.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.23.1...v0.24.0) (2022-10-10) # [0.24.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.23.1...v0.24.0) (2022-10-10)
## [0.23.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.23.0...v0.23.1) (2022-07-05) ## [0.23.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.23.0...v0.23.1) (2022-07-05)
### Bug Fixes ### Bug Fixes
* documentation not generating ([ac226d1](https://gitlab.com/openstapps/projectmanagement/commit/ac226d178a41e24a57f5e24409255aee2cfabf8d)) - documentation not generating ([ac226d1](https://gitlab.com/openstapps/projectmanagement/commit/ac226d178a41e24a57f5e24409255aee2cfabf8d))
# [0.23.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.22.0...v0.23.0) (2022-07-05) # [0.23.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.22.0...v0.23.0) (2022-07-05)
# [0.22.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.21.0...v0.22.0) (2021-12-14) # [0.22.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.21.0...v0.22.0) (2021-12-14)
# [0.21.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.20.1...v0.21.0) (2021-12-14) # [0.21.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.20.1...v0.21.0) (2021-12-14)
### Bug Fixes ### Bug Fixes
* exclude npm audit from maintenance runs ([42e6812](https://gitlab.com/openstapps/projectmanagement/commit/42e68120b7215b6fe0f929955d99907103b7bfed)) - exclude npm audit from maintenance runs ([42e6812](https://gitlab.com/openstapps/projectmanagement/commit/42e68120b7215b6fe0f929955d99907103b7bfed))
* link to old gitlab server ([aa9991a](https://gitlab.com/openstapps/projectmanagement/commit/aa9991ad708dd80b873b738a304c94f6f21dd4ba)), closes [#27](https://gitlab.com/openstapps/projectmanagement/issues/27) - link to old gitlab server ([aa9991a](https://gitlab.com/openstapps/projectmanagement/commit/aa9991ad708dd80b873b738a304c94f6f21dd4ba)), closes [#27](https://gitlab.com/openstapps/projectmanagement/issues/27)
### Features ### Features
* make slack notification optional ([84921ad](https://gitlab.com/openstapps/projectmanagement/commit/84921ad5ce90c24ce53197235e8f8a8aff0f4aba)) - make slack notification optional ([84921ad](https://gitlab.com/openstapps/projectmanagement/commit/84921ad5ce90c24ce53197235e8f8a8aff0f4aba))
## [0.20.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.20.0...v0.20.1) (2020-07-27) ## [0.20.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.20.0...v0.20.1) (2020-07-27)
### Bug Fixes ### Bug Fixes
* ignore archived projects in milestone tidy ([cd768a9](https://gitlab.com/openstapps/projectmanagement/commit/cd768a9dcdb8edc62f8277a45b054e261311e404)) - ignore archived projects in milestone tidy ([cd768a9](https://gitlab.com/openstapps/projectmanagement/commit/cd768a9dcdb8edc62f8277a45b054e261311e404))
* remove invalid project name element ([e4c065d](https://gitlab.com/openstapps/projectmanagement/commit/e4c065d5058512ecab1b7fb173e34d950abb123f)) - remove invalid project name element ([e4c065d](https://gitlab.com/openstapps/projectmanagement/commit/e4c065d5058512ecab1b7fb173e34d950abb123f))
### Features ### Features
* add merge request assignment ([65d05bf](https://gitlab.com/openstapps/projectmanagement/commit/65d05bfe692c7589ae4138439724ba7c0c73bec5)) - add merge request assignment ([65d05bf](https://gitlab.com/openstapps/projectmanagement/commit/65d05bfe692c7589ae4138439724ba7c0c73bec5))
# [0.20.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.19.0...v0.20.0) (2020-05-06) # [0.20.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.19.0...v0.20.0) (2020-05-06)
### Features ### Features
* change builder base image to docker/compose ([5ea40b2](https://gitlab.com/openstapps/projectmanagement/commit/5ea40b2ece8cb2ae764096121492dd9703963f1d)) - change builder base image to docker/compose ([5ea40b2](https://gitlab.com/openstapps/projectmanagement/commit/5ea40b2ece8cb2ae764096121492dd9703963f1d))
# [0.19.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.18.0...v0.19.0) (2020-01-08) # [0.19.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.18.0...v0.19.0) (2020-01-08)
# [0.18.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.17.1...v0.18.0) (2019-11-25) # [0.18.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.17.1...v0.18.0) (2019-11-25)
## [0.17.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.17.0...v0.17.1) (2019-11-19) ## [0.17.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.17.0...v0.17.1) (2019-11-19)
### Features ### Features
* add documentation for automatic publishing ([ab45e35](https://gitlab.com/openstapps/projectmanagement/commit/ab45e35117ef0cca3f42c557e100d4e57a2571df)), closes [#23](https://gitlab.com/openstapps/projectmanagement/issues/23) - add documentation for automatic publishing ([ab45e35](https://gitlab.com/openstapps/projectmanagement/commit/ab45e35117ef0cca3f42c557e100d4e57a2571df)), closes [#23](https://gitlab.com/openstapps/projectmanagement/issues/23)
# [0.17.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.16.1...v0.17.0) (2019-11-13) # [0.17.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.16.1...v0.17.0) (2019-11-13)
### Features ### Features
* update to new meeting date ([98c73b5](https://gitlab.com/openstapps/projectmanagement/commit/98c73b575846fb342c4ffb6a2cf09152a3205f95)) - update to new meeting date ([98c73b5](https://gitlab.com/openstapps/projectmanagement/commit/98c73b575846fb342c4ffb6a2cf09152a3205f95))
## [0.16.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.16.0...v0.16.1) (2019-08-20) ## [0.16.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.16.0...v0.16.1) (2019-08-20)
### Bug Fixes ### Bug Fixes
* add colors to cli log output ([7d271a7](https://gitlab.com/openstapps/projectmanagement/commit/7d271a73648d2367e57a3b6c6bb5725d106dced6)) - add colors to cli log output ([7d271a7](https://gitlab.com/openstapps/projectmanagement/commit/7d271a73648d2367e57a3b6c6bb5725d106dced6))
# [0.16.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.15.0...v0.16.0) (2019-08-20) # [0.16.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.15.0...v0.16.0) (2019-08-20)
### Features ### Features
* only unlabel closed issues before last meeting ([d7b68ae](https://gitlab.com/openstapps/projectmanagement/commit/d7b68ae45c8321f0a1d9cba998b604507445f9fb)) - only unlabel closed issues before last meeting ([d7b68ae](https://gitlab.com/openstapps/projectmanagement/commit/d7b68ae45c8321f0a1d9cba998b604507445f9fb))
# [0.15.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.14.1...v0.15.0) (2019-07-23) # [0.15.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.14.1...v0.15.0) (2019-07-23)
### Features ### Features
* add function and task to get version of used dependency ([067a201](https://gitlab.com/openstapps/projectmanagement/commit/067a2011c03b1e3e9d164844d594e4f332982f12)), closes [#20](https://gitlab.com/openstapps/projectmanagement/issues/20) - add function and task to get version of used dependency ([067a201](https://gitlab.com/openstapps/projectmanagement/commit/067a2011c03b1e3e9d164844d594e4f332982f12)), closes [#20](https://gitlab.com/openstapps/projectmanagement/issues/20)
## [0.14.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.14.0...v0.14.1) (2019-06-05) ## [0.14.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.14.0...v0.14.1) (2019-06-05)
# [0.14.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.13.0...v0.14.0) (2019-06-04) # [0.14.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.13.0...v0.14.0) (2019-06-04)
### Bug Fixes ### Bug Fixes
* fix regression with protected tags ([65a7233](https://gitlab.com/openstapps/projectmanagement/commit/65a723390572f48601e52dac975c9e2b4a0f06c4)) - fix regression with protected tags ([65a7233](https://gitlab.com/openstapps/projectmanagement/commit/65a723390572f48601e52dac975c9e2b4a0f06c4))
### Features ### Features
* add max depth for reminders ([3641ec4](https://gitlab.com/openstapps/projectmanagement/commit/3641ec4d00890f81b24301b61d689cd113e33997)) - add max depth for reminders ([3641ec4](https://gitlab.com/openstapps/projectmanagement/commit/3641ec4d00890f81b24301b61d689cd113e33997))
# [0.13.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.12.1...v0.13.0) (2019-06-03) # [0.13.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.12.1...v0.13.0) (2019-06-03)
### Features ### Features
* add curl to Node.js image ([2ec80fb](https://gitlab.com/openstapps/projectmanagement/commit/2ec80fbb2f1256530e61a389a8444163c7aef8bd)), closes [#18](https://gitlab.com/openstapps/projectmanagement/issues/18) - add curl to Node.js image ([2ec80fb](https://gitlab.com/openstapps/projectmanagement/commit/2ec80fbb2f1256530e61a389a8444163c7aef8bd)), closes [#18](https://gitlab.com/openstapps/projectmanagement/issues/18)
## [0.12.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.12.0...v0.12.1) (2019-05-27) ## [0.12.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.12.0...v0.12.1) (2019-05-27)
# [0.12.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.11.0...v0.12.0) (2019-05-27) # [0.12.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.11.0...v0.12.0) (2019-05-27)
### Bug Fixes ### Bug Fixes
* use user 'node' in Node.js image ([0c8aa72](https://gitlab.com/openstapps/projectmanagement/commit/0c8aa7257add9fd5997105f18b69211152fd8971)), closes [#17](https://gitlab.com/openstapps/projectmanagement/issues/17) - use user 'node' in Node.js image ([0c8aa72](https://gitlab.com/openstapps/projectmanagement/commit/0c8aa7257add9fd5997105f18b69211152fd8971)), closes [#17](https://gitlab.com/openstapps/projectmanagement/issues/17)
### Features ### Features
* add git to node image ([c0058f9](https://gitlab.com/openstapps/projectmanagement/commit/c0058f9c2918bae3e5fce58f11802174ea08084c)), closes [#16](https://gitlab.com/openstapps/projectmanagement/issues/16) - add git to node image ([c0058f9](https://gitlab.com/openstapps/projectmanagement/commit/c0058f9c2918bae3e5fce58f11802174ea08084c)), closes [#16](https://gitlab.com/openstapps/projectmanagement/issues/16)
* notify merge request assignee instead of author ([d60625c](https://gitlab.com/openstapps/projectmanagement/commit/d60625c461c6fe54b7c60091050a31f7f77a958e)) - notify merge request assignee instead of author ([d60625c](https://gitlab.com/openstapps/projectmanagement/commit/d60625c461c6fe54b7c60091050a31f7f77a958e))
# [0.11.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.10.0...v0.11.0) (2019-04-30) # [0.11.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.10.0...v0.11.0) (2019-04-30)
### Features ### Features
* tidy sub groups ([de05b52](https://gitlab.com/openstapps/projectmanagement/commit/de05b52f24985a882dbacdf82cc1a7c3c4efbe88)) - tidy sub groups ([de05b52](https://gitlab.com/openstapps/projectmanagement/commit/de05b52f24985a882dbacdf82cc1a7c3c4efbe88))
# [0.10.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.9.0...v0.10.0) (2019-04-17) # [0.10.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.9.0...v0.10.0) (2019-04-17)
### Features ### Features
* add timestamp to reports ([ad99e45](https://gitlab.com/openstapps/projectmanagement/commit/ad99e45ee3de59de4b5fcca312cb077d90d73335)) - add timestamp to reports ([ad99e45](https://gitlab.com/openstapps/projectmanagement/commit/ad99e45ee3de59de4b5fcca312cb077d90d73335))
# [0.9.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.8.1...v0.9.0) (2019-04-09) # [0.9.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.8.1...v0.9.0) (2019-04-09)
### Features ### Features
* add check for protected tags to tidy task ([feb0c3b](https://gitlab.com/openstapps/projectmanagement/commit/feb0c3ba019b1fdfdc5912db86b20d9cc2dd650f)), closes [#14](https://gitlab.com/openstapps/projectmanagement/issues/14) - add check for protected tags to tidy task ([feb0c3b](https://gitlab.com/openstapps/projectmanagement/commit/feb0c3ba019b1fdfdc5912db86b20d9cc2dd650f)), closes [#14](https://gitlab.com/openstapps/projectmanagement/issues/14)
## [0.8.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.8.0...v0.8.1) (2019-03-20) ## [0.8.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.8.0...v0.8.1) (2019-03-20)
# [0.8.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.7.0...v0.8.0) (2019-03-20) # [0.8.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.7.0...v0.8.0) (2019-03-20)
### Features ### Features
* use mustache instead of tangular ([95f7521](https://gitlab.com/openstapps/projectmanagement/commit/95f7521dbddc13a6acb5835ae05dd5535cce067f)) - use mustache instead of tangular ([95f7521](https://gitlab.com/openstapps/projectmanagement/commit/95f7521dbddc13a6acb5835ae05dd5535cce067f))
# [0.7.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.6.0...v0.7.0) (2019-03-20) # [0.7.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.6.0...v0.7.0) (2019-03-20)
# [0.6.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.5.0...v0.6.0) (2019-02-25) # [0.6.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.5.0...v0.6.0) (2019-02-25)
### Features ### Features
* adjust reminder for unfixed merge requests ([e02939c](https://gitlab.com/openstapps/projectmanagement/commit/e02939c470ff9a63fb33c9754e06ea4dfbbd9b15)) - adjust reminder for unfixed merge requests ([e02939c](https://gitlab.com/openstapps/projectmanagement/commit/e02939c470ff9a63fb33c9754e06ea4dfbbd9b15))
# [0.5.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.4.1...v0.5.0) (2019-02-05) # [0.5.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.4.1...v0.5.0) (2019-02-05)
### Features ### Features
* add new task to remind about open merge requests ([201ec09](https://gitlab.com/openstapps/projectmanagement/commit/201ec093b7ab676d5d7a84972c5d9e64664af7e0)) - add new task to remind about open merge requests ([201ec09](https://gitlab.com/openstapps/projectmanagement/commit/201ec093b7ab676d5d7a84972c5d9e64664af7e0))
## [0.4.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.4.0...v0.4.1) (2019-01-30) ## [0.4.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.4.0...v0.4.1) (2019-01-30)
# [0.4.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.3.0...v0.4.0) (2019-01-30) # [0.4.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.3.0...v0.4.0) (2019-01-30)
### Features ### Features
* add slack reminder for open merge requests ([47e9775](https://gitlab.com/openstapps/projectmanagement/commit/47e977583bc036d48194d1e6bc9e6214dbbe6062)) - add slack reminder for open merge requests ([47e9775](https://gitlab.com/openstapps/projectmanagement/commit/47e977583bc036d48194d1e6bc9e6214dbbe6062))
# [0.3.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.2.2...v0.3.0) (2019-01-28) # [0.3.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.2.2...v0.3.0) (2019-01-28)
### Features ### Features
* include sub groups in report generation ([040c666](https://gitlab.com/openstapps/projectmanagement/commit/040c666e747df35b773be1a0e30b7ba99f3b6598)) - include sub groups in report generation ([040c666](https://gitlab.com/openstapps/projectmanagement/commit/040c666e747df35b773be1a0e30b7ba99f3b6598))
## [0.2.2](https://gitlab.com/openstapps/projectmanagement/compare/v0.2.1...v0.2.2) (2019-01-23) ## [0.2.2](https://gitlab.com/openstapps/projectmanagement/compare/v0.2.1...v0.2.2) (2019-01-23)
### Bug Fixes ### Bug Fixes
* correctly determine project for issue ([8f06fc3](https://gitlab.com/openstapps/projectmanagement/commit/8f06fc35347f06e8409e4190079323af91fc0a37)) - correctly determine project for issue ([8f06fc3](https://gitlab.com/openstapps/projectmanagement/commit/8f06fc35347f06e8409e4190079323af91fc0a37))
## [0.2.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.2.0...v0.2.1) (2019-01-17) ## [0.2.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.2.0...v0.2.1) (2019-01-17)
### Bug Fixes ### Bug Fixes
* correctly add shebang line to cli ([f2933ef](https://gitlab.com/openstapps/projectmanagement/commit/f2933ef9e23e6fa92086800d791c9e4c6685fd4b)) - correctly add shebang line to cli ([f2933ef](https://gitlab.com/openstapps/projectmanagement/commit/f2933ef9e23e6fa92086800d791c9e4c6685fd4b))
# [0.2.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.1.1...v0.2.0) (2019-01-17) # [0.2.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.1.1...v0.2.0) (2019-01-17)
### Bug Fixes ### Bug Fixes
* correct link for projects ([a95ec14](https://gitlab.com/openstapps/projectmanagement/commit/a95ec14be8e169ee1e9f84e3ef98a9712babd302)) - correct link for projects ([a95ec14](https://gitlab.com/openstapps/projectmanagement/commit/a95ec14be8e169ee1e9f84e3ef98a9712babd302))
* do not add issues to report from backlog ([e902d70](https://gitlab.com/openstapps/projectmanagement/commit/e902d705bf66e07f70a9c34f9f13160c5db5982f)) - do not add issues to report from backlog ([e902d70](https://gitlab.com/openstapps/projectmanagement/commit/e902d705bf66e07f70a9c34f9f13160c5db5982f))
* unlabel issues with label meeting only ([41a349e](https://gitlab.com/openstapps/projectmanagement/commit/41a349ead50db256916f5506829e587d1ecb4a43)) - unlabel issues with label meeting only ([41a349e](https://gitlab.com/openstapps/projectmanagement/commit/41a349ead50db256916f5506829e587d1ecb4a43))
## [0.1.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.1.0...v0.1.1) (2019-01-07) ## [0.1.1](https://gitlab.com/openstapps/projectmanagement/compare/v0.1.0...v0.1.1) (2019-01-07)
### Bug Fixes ### Bug Fixes
* remove failing deletion of labels ([ac8f2e1](https://gitlab.com/openstapps/projectmanagement/commit/ac8f2e10f965ef0e4950d8f294b6c34a26f4bad0)) - remove failing deletion of labels ([ac8f2e1](https://gitlab.com/openstapps/projectmanagement/commit/ac8f2e10f965ef0e4950d8f294b6c34a26f4bad0))
# [0.1.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.0.2...v0.1.0) (2018-12-13) # [0.1.0](https://gitlab.com/openstapps/projectmanagement/compare/v0.0.2...v0.1.0) (2018-12-13)
## [0.0.2](https://gitlab.com/openstapps/projectmanagement/compare/v0.0.1...v0.0.2) (2018-12-03) ## [0.0.2](https://gitlab.com/openstapps/projectmanagement/compare/v0.0.1...v0.0.2) (2018-12-03)
### Features ### Features
* add issue template for features ([8ae1a51](https://gitlab.com/openstapps/projectmanagement/commit/8ae1a5166962c5d0b41b869c6021ddc456407022)) - add issue template for features ([8ae1a51](https://gitlab.com/openstapps/projectmanagement/commit/8ae1a5166962c5d0b41b869c6021ddc456407022))
* add nyc configuration ([883ab9b](https://gitlab.com/openstapps/projectmanagement/commit/883ab9bfa55def9a0a8dd4ee5427c1eb9ad2960b)) - add nyc configuration ([883ab9b](https://gitlab.com/openstapps/projectmanagement/commit/883ab9bfa55def9a0a8dd4ee5427c1eb9ad2960b))
## [0.0.1](https://gitlab.com/openstapps/projectmanagement/compare/19fd0f6e4cb8b44152333242aa17db84b2bd038a...v0.0.1) (2018-11-29) ## [0.0.1](https://gitlab.com/openstapps/projectmanagement/compare/19fd0f6e4cb8b44152333242aa17db84b2bd038a...v0.0.1) (2018-11-29)
### Features ### Features
* add projectmanagement and pack ([19fd0f6](https://gitlab.com/openstapps/projectmanagement/commit/19fd0f6e4cb8b44152333242aa17db84b2bd038a)) - add projectmanagement and pack ([19fd0f6](https://gitlab.com/openstapps/projectmanagement/commit/19fd0f6e4cb8b44152333242aa17db84b2bd038a))

View File

@@ -2,16 +2,16 @@
To contribute effectively to this or other Open StApps projects you should read the documentation in this repository first. To contribute effectively to this or other Open StApps projects you should read the documentation in this repository first.
* What's the scope of one [project](../project-docs/workflow/PROJECT.md)? - What's the scope of one [project](../project-docs/workflow/PROJECT.md)?
* How to create [issues](../project-docs/workflow/ISSUES.md) in projects. - How to create [issues](../project-docs/workflow/ISSUES.md) in projects.
* How to [set up](../project-docs/SETUP.md) your machine? - How to [set up](../project-docs/SETUP.md) your machine?
* How to contribute code? - How to contribute code?
* What's the [branching model](../project-docsworkflow/BRANCHING.md)? - What's the [branching model](../project-docsworkflow/BRANCHING.md)?
* How to write [code](../project-docs/workflow/CODING.md)? - How to write [code](../project-docs/workflow/CODING.md)?
* How to create [merge requests](../project-docs/workflow/MERGING.md)? - How to create [merge requests](../project-docs/workflow/MERGING.md)?
* How to write [documentation](../project-docs/workflow/DOCUMENTATION.md)? - How to write [documentation](../project-docs/workflow/DOCUMENTATION.md)?
* How to [version](../project-docs/workflow/VERSIONING.md) the code? - How to [version](../project-docs/workflow/VERSIONING.md) the code?
## Further resources ## Further resources
* [Docker cheat sheet](../project-docs/DOCKER_CHEAT_SHEET.md) - [Docker cheat sheet](../project-docs/DOCKER_CHEAT_SHEET.md)

View File

@@ -1,6 +1,6 @@
# @openstapps/projectmanagement # @openstapps/projectmanagement
[![pipeline status](https://img.shields.io/gitlab/pipeline/openstapps/projectmanagement.svg?style=flat-square)](https://gitlab.com/openstapps/projectmanagement/commits/master) [![pipeline status](https://img.shields.io/gitlab/pipeline/openstapps/projectmanagement.svg?style=flat-square)](https://gitlab.com/openstapps/projectmanagement/commits/master)
[![npm](https://img.shields.io/npm/v/@openstapps/projectmanagement.svg?style=flat-square)](https://npmjs.com/package/@openstapps/projectmanagement) [![npm](https://img.shields.io/npm/v/@openstapps/projectmanagement.svg?style=flat-square)](https://npmjs.com/package/@openstapps/projectmanagement)
[![license)](https://img.shields.io/npm/l/@openstapps/projectmanagement.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![license)](https://img.shields.io/npm/l/@openstapps/projectmanagement.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![documentation](https://img.shields.io/badge/documentation-online-blue.svg?style=flat-square)](https://openstapps.gitlab.io/projectmanagement) [![documentation](https://img.shields.io/badge/documentation-online-blue.svg?style=flat-square)](https://openstapps.gitlab.io/projectmanagement)
@@ -21,7 +21,7 @@ Every school gets their own subgroup on GitLab inside the group `@openstapps`.
The technical name is the license plate of the school. This can be found in the [list of license plates](project-docs/SCHOOL_IDENTIFIERS.md). The technical name is the license plate of the school. This can be found in the [list of license plates](project-docs/SCHOOL_IDENTIFIERS.md).
The official name of the school is used as the group's name and a matching logo is picked to act as the group's logo - this should be a square one because of the way that GitLab handles/shows logos. The official name of the school is used as the group's name and a matching logo is picked to act as the group's logo - this should be a square one because of the way that GitLab handles/shows logos.
#### Permissions on the group #### Permissions on the group

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./lib/cli.js')

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,9 @@
{ {
"name": "@openstapps/projectmanagement", "name": "@openstapps/projectmanagement",
"version": "0.25.0",
"description": "Main documentation and scripts for maintenance.", "description": "Main documentation and scripts for maintenance.",
"repository": { "version": "2.1.0",
"type": "git", "license": "GPL-3.0-only",
"url": "git@gitlab.com:openstapps/projectmanagement.git" "repository": "git@gitlab.com:openstapps/projectmanagement.git",
},
"scripts": {
"build": "npm run lint && npm run compile",
"check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'",
"documentation": "typedoc --out docs --readme README.md --listInvalidSymbolLinks --entryPointStrategy expand src",
"prepublishOnly": "npm ci && npm run build && npm run test",
"preversion": "npm run prepublishOnly",
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "nyc mocha --require ts-node/register 'test/**/*.spec.ts'",
"lint": "eslint --ext .ts src/"
},
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>", "author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"contributors": [ "contributors": [
"Anselm Stordeur <anselmstordeur@gmail.com>", "Anselm Stordeur <anselmstordeur@gmail.com>",
@@ -26,54 +12,64 @@
"Rainer Killinger <mail-openstapps@killinger.co>", "Rainer Killinger <mail-openstapps@killinger.co>",
"Thea Schöbl <dev@theaninova.de>" "Thea Schöbl <dev@theaninova.de>"
], ],
"license": "GPL-3.0-only", "prettier": "@openstapps/prettier-config",
"main": "lib/common.js",
"bin": {
"openstapps-projectmanagement": "app.js"
},
"scripts": {
"build": "npm run lint && npm run compile",
"check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc",
"documentation": "typedoc --out docs --readme README.md --listInvalidSymbolLinks --entryPointStrategy expand src",
"format:fix": "prettier --write .",
"lint": "eslint --ext .ts src/",
"prepublishOnly": "npm ci && npm run build && npm run test",
"preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"",
"test": "nyc mocha --require ts-node/register 'test/**/*.spec.ts'",
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md"
},
"dependencies": { "dependencies": {
"@krlwlfrt/async-pool": "0.4.1", "@openstapps/gitlab-api": "workspace:*",
"@openstapps/gitlab-api": "0.10.0", "@openstapps/logger": "workspace:*",
"@openstapps/logger": "1.1.1", "@slack/web-api": "6.8.1",
"@slack/client": "5.0.2", "commander": "10.0.0",
"@types/glob": "7.2.0", "date-fns": "2.29.3",
"@types/mustache": "4.2.2",
"@types/node": "14.18.36",
"@types/tmp": "0.2.3",
"commander": "9.5.0",
"glob": "8.1.0", "glob": "8.1.0",
"moment": "2.29.4",
"mustache": "4.2.0", "mustache": "4.2.0",
"tmp": "0.2.1" "tmp": "0.2.1"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.33.0", "@openstapps/configuration": "workspace:*",
"@openstapps/eslint-config": "1.1.0", "@openstapps/eslint-config": "workspace:*",
"@openstapps/prettier-config": "workspace:*",
"@testdeck/mocha": "0.3.3", "@testdeck/mocha": "0.3.3",
"@types/chai": "4.3.4", "@types/chai": "4.3.4",
"@types/chai-as-promised": "7.1.5", "@types/chai-as-promised": "7.1.5",
"@types/glob": "8.0.1",
"@types/mocha": "10.0.1", "@types/mocha": "10.0.1",
"@typescript-eslint/eslint-plugin": "5.39.0", "@types/mustache": "4.2.2",
"@typescript-eslint/parser": "5.39.0", "@types/node": "18.15.3",
"@types/tmp": "0.2.3",
"@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.49.0",
"chai": "4.3.7", "chai": "4.3.7",
"chai-as-promised": "7.1.1", "chai-as-promised": "7.1.1",
"conventional-changelog-cli": "2.2.2", "conventional-changelog-cli": "2.2.2",
"eslint": "8.32.0", "eslint": "8.33.0",
"eslint-config-prettier": "8.6.0", "eslint-config-prettier": "8.6.0",
"eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-jsdoc": "39.7.4",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "44.0.2", "eslint-plugin-unicorn": "45.0.2",
"mocha": "10.2.0", "mocha": "10.2.0",
"nyc": "15.1.0", "nyc": "15.1.0",
"prepend-file-cli": "1.0.6",
"prettier": "2.8.3", "prettier": "2.8.3",
"rimraf": "3.0.2", "rimraf": "4.4.0",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"tslint": "6.1.3",
"typedoc": "0.22.18", "typedoc": "0.22.18",
"typescript": "4.4.4" "typescript": "4.4.4"
}, },
"main": "lib/common.js",
"typings": "lib/common.d.ts",
"bin": {
"openstapps-projectmanagement": "lib/cli.js"
},
"nyc": { "nyc": {
"all": true, "all": true,
"branches": 95, "branches": 95,
@@ -98,5 +94,6 @@
"ts-node/register" "ts-node/register"
], ],
"statements": 95 "statements": 95
} },
"typings": "lib/common.d.ts"
} }

View File

@@ -42,6 +42,6 @@ Please consult the [Docker documentation](https://docs.docker.com/engine/referen
## Further resources ## Further resources
* [An Exhaustive Guide to Writing Dockerfiles for Node.js Web Apps](https://blog.hasura.io/an-exhaustive-guide-to-writing-dockerfiles-for-node-js-web-apps-bbee6bd2f3c4) - [An Exhaustive Guide to Writing Dockerfiles for Node.js Web Apps](https://blog.hasura.io/an-exhaustive-guide-to-writing-dockerfiles-for-node-js-web-apps-bbee6bd2f3c4)
* [Docker and Node.js Best Practices - [Docker and Node.js Best Practices
](https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md) ](https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md)

View File

@@ -2,11 +2,11 @@
## Required tools/packages ## Required tools/packages
* [SSH](https://docs.gitlab.com/ee/ssh/README.html) - [SSH](https://docs.gitlab.com/ee/ssh/README.html)
* [Git](https://docs.gitlab.com/ee/gitlab-basics/start-using-git.html) - [Git](https://docs.gitlab.com/ee/gitlab-basics/start-using-git.html)
* [Docker](https://docs.gitlab.com/ee/user/project/container_registry.html) & Docker-Compose - [Docker](https://docs.gitlab.com/ee/user/project/container_registry.html) & Docker-Compose
* Node.js & NPM - be sure to install the "Fermium" (14.x) LTS version, might need a [PPA](https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions) or use NVM - Node.js & NPM - be sure to install the "Fermium" (14.x) LTS version, might need a [PPA](https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions) or use NVM
* [NVM](https://github.com/creationix/nvm#installation) - if you want to use multiple different versions of node - [NVM](https://github.com/creationix/nvm#installation) - if you want to use multiple different versions of node
Example for Debian based distributions: Example for Debian based distributions:
@@ -16,13 +16,13 @@ apt install ssh git docker docker-compose nodejs
## IDE - recommended choices ## IDE - recommended choices
* [VSCode](https://code.visualstudio.com/) - [VSCode](https://code.visualstudio.com/)
* [Webstorm](https://www.jetbrains.com/webstorm/download/) - Educational or OpenSource License **cannot** be used - [Webstorm](https://www.jetbrains.com/webstorm/download/) - Educational or OpenSource License **cannot** be used
## Optional helpful tools ## Optional helpful tools
* Curl - for executing HTTP requests on the command line - Curl - for executing HTTP requests on the command line
* Postman or Insomnia - for executing HTTP requests with a GUI - Postman or Insomnia - for executing HTTP requests with a GUI
## Before you start (Windows only) ## Before you start (Windows only)
@@ -31,8 +31,8 @@ Depending on your preferred settings you could consider applying `git config cor
## Clone starter repositories ## Clone starter repositories
* [Minimal deployment](https://gitlab.com/openstapps/minimal-deployment) - contains backend, database, minimal connector, copy (from api) and app - [Minimal deployment](https://gitlab.com/openstapps/minimal-deployment) - contains backend, database, minimal connector, copy (from api) and app
* [Minimal connector](https://gitlab.com/openstapps/minimal-connector) - an example connector to learn the principles of connector development - [Minimal connector](https://gitlab.com/openstapps/minimal-connector) - an example connector to learn the principles of connector development
```shell ```shell
git clone git@gitlab.com:openstapps/minimal-deployment.git git clone git@gitlab.com:openstapps/minimal-deployment.git

View File

@@ -2,9 +2,9 @@
## Possible branch names ## Possible branch names
* `master` - `master`
* `develop` - `develop`
* `$ID-$TITLE` - `$ID-$TITLE`
### `master` ### `master`
@@ -22,4 +22,4 @@ Development of features, bugfixes or refactoring is done in branches referencing
## Further resources ## Further resources
* [Git - A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) - [Git - A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/)

View File

@@ -16,6 +16,6 @@ Inline comments should not explain what code from external projects is doing. Ad
## Further resources ## Further resources
* [Commit guidelines](COMMITS.md) - [Commit guidelines](COMMITS.md)
* [TypeScript coding guidelines](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines) - [TypeScript coding guidelines](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines)
* [Licensing guidelines](LICENSING.md) - [Licensing guidelines](LICENSING.md)

View File

@@ -5,24 +5,24 @@
Commit subjects should match the following template: Commit subjects should match the following template:
> `TYPE`: `SUBJECT` > `TYPE`: `SUBJECT`
> >
> `DESCRIPTION` > `DESCRIPTION`
### `TYPE` ### `TYPE`
`TYPE` can have one of the following values: `TYPE` can have one of the following values:
| value | meaning | | value | meaning |
| --- | --- | | -------- | ----------------------------------------------------------------------------------------------------------- |
| build | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) | | build | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) |
| ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) | | ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) |
| docs | Documentation only changes | | docs | Documentation only changes |
| feat | A new feature | | feat | A new feature |
| fix | A bug fix | | fix | A bug fix |
| perf | A code change that improves performance | | perf | A code change that improves performance |
| refactor | A code change that neither fixes a bug nor adds a feature | | refactor | A code change that neither fixes a bug nor adds a feature |
| style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) | | style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) |
| test | Adding missing tests or correcting existing tests | | test | Adding missing tests or correcting existing tests |
These are the [types](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type), that angular uses. They are easily adaptable for all TypeScript based web related projects. These are the [types](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type), that angular uses. They are easily adaptable for all TypeScript based web related projects.
@@ -56,5 +56,5 @@ conventional-changelog -p angular -i CHANGELOG.md -s -r 0
## Further resources ## Further resources
* [Recommendations on commit messages](https://chris.beams.io/posts/git-commit/) - [Recommendations on commit messages](https://chris.beams.io/posts/git-commit/)
* [Versioning](VERSIONING.md) - [Versioning](VERSIONING.md)

View File

@@ -10,17 +10,17 @@ Code is annotated with [TypeDoc](https://typedoc.org/), which then can be used t
Please follow these guidelines to reduce redundance: Please follow these guidelines to reduce redundance:
* The purpose of inline documentation is to explain what the line of code actually does without explaining syntax or external references. Example: - The purpose of inline documentation is to explain what the line of code actually does without explaining syntax or external references. Example:
```typescript ```typescript
// extend the template by the properties of the base template // extend the template by the properties of the base template
templateBase.properties = mergeObjects( templateBase.properties = mergeObjects(
templateBase.properties, templateBase.properties,
templates['base.template.json'].mappings._default_.properties templates['base.template.json'].mappings._default_.properties,
); );
``` ```
* Follow the [recommendations](http://typedoc.org/guides/doccomments/) of `typedoc`, because it is used to generate documentation. Do not denote the types again in the documentation (`@param` and `@returns`), because they are already "documented" in the code itself. Example: - Follow the [recommendations](http://typedoc.org/guides/doccomments/) of `typedoc`, because it is used to generate documentation. Do not denote the types again in the documentation (`@param` and `@returns`), because they are already "documented" in the code itself. Example:
```typescript ```typescript
/** /**
@@ -39,8 +39,8 @@ async get<T>(key: string): Promise<T> {
### Inline Comments `//` ### Inline Comments `//`
* Start inline comments with a lowercase letter and a space after the `//` - Start inline comments with a lowercase letter and a space after the `//`
* Place the comment above the line that it is referencing - Place the comment above the line that it is referencing
```typescript ```typescript
// lorem ipsum // lorem ipsum
@@ -49,14 +49,14 @@ const lorem;
### Doc Comments `/**` ### Doc Comments `/**`
* Start with a capital letter - Start with a capital letter
* Keep the first line short - Keep the first line short
* The first line should not end with a period, nor should it consist of multiple sentences - The first line should not end with a period, nor should it consist of multiple sentences
* The first line should be easily scannable - The first line should be easily scannable
* If you want to comment more than one line, do a short summary in the first line, then continue after a blank line with the more detailed description - If you want to comment more than one line, do a short summary in the first line, then continue after a blank line with the more detailed description
* Document all parameters in functions using `@param` - Document all parameters in functions using `@param`
* `@param` must not contain a type annotation, just `@param name description` - `@param` must not contain a type annotation, just `@param name description`
* Do not include `@return`, as it is redundant information - Do not include `@return`, as it is redundant information
```typescript ```typescript
/** /**
@@ -81,4 +81,4 @@ The path `.gitlab/issue_templates` can contain markdown files which act as templ
## Further resources ## Further resources
* [GitLab issue templates](https://gitlab.com/help/user/project/description_templates.md) - [GitLab issue templates](https://gitlab.com/help/user/project/description_templates.md)

View File

@@ -6,4 +6,4 @@ Issues keep a discussion of the topic, are related to a [branch](BRANCHING.md),
## Further resources ## Further resources
* [Always start with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/) - [Always start with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)

View File

@@ -45,4 +45,4 @@ If the file is updated in a new year after its creation, the `<year>` should be
## Further resources ## Further resources
* [Coding style](CODING.md) - [Coding style](CODING.md)

View File

@@ -7,8 +7,8 @@ A merge request can also gather the work of multiple issues.
Merge requests are used to merge the changes to the main branches (`master`, `develop`). Every merge request needs to be reviewed/commented by at least 2 other developers before they can be accepted. This ensure that all guidelines are followed and that the scope of the issue is matched. Merge requests are used to merge the changes to the main branches (`master`, `develop`). Every merge request needs to be reviewed/commented by at least 2 other developers before they can be accepted. This ensure that all guidelines are followed and that the scope of the issue is matched.
Before any merge of an `issue`-branch into the according `master`-branch, the commits of the `issue`-branch shall be tidied up. Before any merge of an `issue`-branch into the according `master`-branch, the commits of the `issue`-branch shall be tidied up.
Unstage your commits from the `issue`-branch (`git reset master`). Create your new commit(s) and push them to the remote repository (`git push --force`). Unstage your commits from the `issue`-branch (`git reset master`). Create your new commit(s) and push them to the remote repository (`git push --force`).
## Further resources ## Further resources
* [Commits](COMMITS.md) - [Commits](COMMITS.md)

View File

@@ -33,5 +33,5 @@ For the contents and purpose of `.editorconfig`, `tsconfig.json` and `tslint.jso
## Further resources ## Further resources
* [Coding style](CODING.md) - [Coding style](CODING.md)
* [Versioning](VERSIONING.md) - [Versioning](VERSIONING.md)

View File

@@ -2,7 +2,7 @@
## NPM ## NPM
All projects that use the [OpenStApps configuration](https://gitlab.com/openstapps/configuration) and implement the automatic publishing via GitLab CI can simply be published to the NPM registry by using the appropriate job. It uses the `NPM_AUTH_TOKEN` that is a [protected variable](https://gitlab.com/groups/openstapps/-/settings/ci_cd) of the OpenStApps group. To trigger this job, a new tag has to be created (on the master branch): All projects that use the [OpenStApps configuration](https://gitlab.com/openstapps/configuration) and implement the automatic publishing via GitLab CI can simply be published to the NPM registry by using the appropriate job. It uses the `NPM_AUTH_TOKEN` that is a [protected variable](https://gitlab.com/groups/openstapps/-/settings/ci_cd) of the OpenStApps group. To trigger this job, a new tag has to be created (on the master branch):
### Use current [master branch](BRANCHING.md) ### Use current [master branch](BRANCHING.md)
@@ -19,8 +19,8 @@ npm version (major|minor|patch)
After these steps there should 2 new commits: After these steps there should 2 new commits:
* A version commit - A version commit
* A changelog commit - A changelog commit
Also a new git tag should be created. Also a new git tag should be created.

View File

@@ -2,9 +2,9 @@
We use semantic version through `npm`. We use semantic version through `npm`.
* `npm version patch`: For fixes/patches - `npm version patch`: For fixes/patches
* `npm version minor`: For new features - `npm version minor`: For new features
* `npm version major`: For breaking changes in the API - `npm version major`: For breaking changes in the API
Or directly with `git tag vMAJOR.MINOR.PATCH`. Or directly with `git tag vMAJOR.MINOR.PATCH`.
@@ -12,4 +12,4 @@ This tag is set on the `develop`- (if present) or on the `master`-[branch](BRANC
## Further resources ## Further resources
* [Commit guidelines](COMMITS.md) - [Commit guidelines](COMMITS.md)

View File

@@ -17,15 +17,15 @@
}, },
{ {
"matchUpdateTypes": ["major"], "matchUpdateTypes": ["major"],
"matchPackagePatterns": ["^@angular", "^@ionic", "^@types\/node$", "^got$"], "matchPackagePatterns": ["^@angular", "^@ionic", "^@types/node$", "^got$"],
"enabled": false "enabled": false
}, },
{ {
"matchPackagePatterns": ["^@types\/geojson$"], "matchPackagePatterns": ["^@types/geojson$"],
"allowedVersions": "1.0.6" "allowedVersions": "1.0.6"
}, },
{ {
"matchPackagePatterns": ["^@elastic\/elasticsearch$"], "matchPackagePatterns": ["^@elastic/elasticsearch$"],
"allowedVersions": "<6.0" "allowedVersions": "<6.0"
}, },
{ {
@@ -41,18 +41,12 @@
"allowedVersions": "<5.0" "allowedVersions": "<5.0"
}, },
{ {
"matchDepTypes": [ "matchDepTypes": ["peerDependencies"],
"peerDependencies"
],
"rangeStrategy": "replace" "rangeStrategy": "replace"
} }
], ],
"lockFileMaintenance": { "lockFileMaintenance": {
"enabled": true "enabled": true
}, },
"reviewers": [ "reviewers": ["abcdev", "jovankrunic", "theaninova"]
"abcdev",
"jovankrunic",
"theaninova"
]
} }

View File

@@ -12,23 +12,9 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {asyncPool} from '@krlwlfrt/async-pool';
import {Api} from '@openstapps/gitlab-api'; import {Api} from '@openstapps/gitlab-api';
import {Group, Project} from '@openstapps/gitlab-api/lib/types'; import {Project} from '@openstapps/gitlab-api/lib/types';
import {Logger} from '@openstapps/logger'; import {Logger} from '@openstapps/logger';
import {readFile, writeFile} from 'fs';
import {promisify} from 'util';
import {CONCURRENCY} from './configuration';
/**
* Promisified version of readFile
*/
export const readFilePromisified = promisify(readFile);
/**
* Promisified version of writeFile
*/
export const writeFilePromisified = promisify(writeFile);
/** /**
* Get projects for a list of groups * Get projects for a list of groups
@@ -39,37 +25,10 @@ export const writeFilePromisified = promisify(writeFile);
export async function getProjects(api: Api, groups: number[]): Promise<Project[]> { export async function getProjects(api: Api, groups: number[]): Promise<Project[]> {
Logger.info(`Fetching all projects for specified groups (${groups.length})...`); Logger.info(`Fetching all projects for specified groups (${groups.length})...`);
const projectResults = await asyncPool(CONCURRENCY, groups, async groupId => { const projectResults = await Promise.all(groups.map(api.getProjectsForGroup));
return api.getProjectsForGroup(groupId); const projects = projectResults.flat();
});
const projects = flatten2dArray(projectResults);
Logger.log(`Fetched ${projects.length} project(s).`); Logger.log(`Fetched ${projects.length} project(s).`);
return projects; return projects;
} }
/**
* Get subgroups for a list of groups
*
* @param api GitLab API to make requests with
* @param groups List of groups
*/
export async function getSubGroups(api: Api, groups: number[]): Promise<Group[]> {
return flatten2dArray(
await asyncPool(CONCURRENCY, groups, async groupId => {
return api.getSubGroupsForGroup(groupId);
}),
);
}
/**
* Flatten 2d array
*
* @param array Flattened array
*/
export function flatten2dArray<T>(array: T[][]): T[] {
// eslint-disable-next-line unicorn/prefer-spread
return ([] as T[]).concat(...array);
}

View File

@@ -13,7 +13,9 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Label} from '@openstapps/gitlab-api/lib/types'; import {Label} from '@openstapps/gitlab-api/lib/types';
import moment from 'moment'; import setHours from 'date-fns/setHours';
import nextThursday from 'date-fns/nextThursday';
import previousThursday from 'date-fns/previousThursday';
/** /**
* List of schools with their IDs * List of schools with their IDs
@@ -183,31 +185,11 @@ export const NOTE_PREFIX = '`openstapps/projectmanagement`';
*/ */
export const SLACK_CHANNEL = 'C762UG76Z'; export const SLACK_CHANNEL = 'C762UG76Z';
/**
* Concurrency for async pool
*/
export const CONCURRENCY = 3;
/** /**
* Maximum depth for merge request reminders * Maximum depth for merge request reminders
*/ */
export const MAX_DEPTH_FOR_REMINDER = 2; export const MAX_DEPTH_FOR_REMINDER = 2;
/** export const NEXT_MEETING = setHours(nextThursday(Date.now()), 10);
* Next meeting
*/
export const NEXT_MEETING = moment()
.startOf('week')
// tslint:disable-next-line:no-magic-numbers
.hour(10)
// tslint:disable-next-line:no-magic-numbers
.day(3);
if (NEXT_MEETING.isBefore(moment())) { export const LAST_MEETING = setHours(previousThursday(Date.now()), 10);
NEXT_MEETING.add(1, 'week');
}
/**
* Last meeting
*/
export const LAST_MEETING = moment(NEXT_MEETING).subtract(1, 'week');

View File

@@ -14,7 +14,7 @@
*/ */
import {existsSync, PathLike} from 'fs'; import {existsSync, PathLike} from 'fs';
import path from 'path'; import path from 'path';
import {readFilePromisified} from '../common'; import {readFile} from 'fs/promises';
/** /**
* Get used version of a dependency of a project referenced by a path * Get used version of a dependency of a project referenced by a path
@@ -27,7 +27,7 @@ export async function getUsedVersion(directoryPath: PathLike, dependency: string
throw new Error(`'package.json' does not exist in '${directoryPath}'. Not a Node.js project?`); throw new Error(`'package.json' does not exist in '${directoryPath}'. Not a Node.js project?`);
} }
const buffer = await readFilePromisified(path.join(directoryPath.toString(), 'package.json')); const buffer = await readFile(path.join(directoryPath.toString(), 'package.json'));
const content = buffer.toString(); const content = buffer.toString();
const packageJson = JSON.parse(content); const packageJson = JSON.parse(content);

View File

@@ -12,7 +12,6 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {asyncPool} from '@krlwlfrt/async-pool';
import {Api} from '@openstapps/gitlab-api'; import {Api} from '@openstapps/gitlab-api';
import { import {
AccessLevel, AccessLevel,
@@ -23,8 +22,8 @@ import {
User, User,
} from '@openstapps/gitlab-api/lib/types'; } from '@openstapps/gitlab-api/lib/types';
import {Logger} from '@openstapps/logger'; import {Logger} from '@openstapps/logger';
import {WebClient} from '@slack/client'; import {WebClient} from '@slack/web-api';
import {CONCURRENCY, GROUPS, MAX_DEPTH_FOR_REMINDER, NOTE_PREFIX, SLACK_CHANNEL} from '../configuration'; import {GROUPS, MAX_DEPTH_FOR_REMINDER, NOTE_PREFIX, SLACK_CHANNEL} from '../configuration';
/** /**
* Remind people of open merge requests * Remind people of open merge requests
@@ -55,9 +54,9 @@ export async function remind(api: Api): Promise<void> {
// instantiate slack client // instantiate slack client
const client = const client =
typeof process.env.SLACK_API_TOKEN !== 'undefined' typeof process.env.SLACK_API_TOKEN === 'undefined'
? new WebClient(process.env.SLACK_API_TOKEN) ? undefined
: undefined; : new WebClient(process.env.SLACK_API_TOKEN);
// get members of main group // get members of main group
const members = await api.getMembers(MembershipScope.GROUPS, GROUPS[0]); const members = await api.getMembers(MembershipScope.GROUPS, GROUPS[0]);
@@ -75,115 +74,117 @@ export async function remind(api: Api): Promise<void> {
Logger.info(`Found ${maintainers.length} maintainer(s).`); Logger.info(`Found ${maintainers.length} maintainer(s).`);
await asyncPool(CONCURRENCY, mergeRequests, async mergeRequest => { await Promise.all(
// check if merge request is WIP mergeRequests.map(async mergeRequest => {
if (mergeRequest.work_in_progress) { // check if merge request is WIP
Logger.info(`Merge request '${mergeRequest.title}' is WIP.`); if (mergeRequest.work_in_progress) {
Logger.info(`Merge request '${mergeRequest.title}' is WIP.`);
return; return;
}
// get merge request approval
const approval = await api.getMergeRequestApproval(mergeRequest.project_id, mergeRequest.iid);
// get merge request discussions
const discussions = await api.getMergeRequestDiscussions(mergeRequest.project_id, mergeRequest.iid);
// check if at least one of the discussions is unresolved
const hasUnresolvedDiscussions = discussions.some(discussion => {
return discussion.notes.some(note => {
return note.resolvable && (typeof note.resolved === 'undefined' || !note.resolved);
});
});
if (hasUnresolvedDiscussions) {
let recipient = mergeRequest.author.username;
if (typeof mergeRequest.assignee !== 'undefined' && mergeRequest.assignee !== null) {
recipient = mergeRequest.assignee.username;
} }
// create note in merge request // get merge request approval
await api.createNote( const approval = await api.getMergeRequestApproval(mergeRequest.project_id, mergeRequest.iid);
mergeRequest.project_id,
Scope.MERGE_REQUESTS,
mergeRequest.iid,
`${NOTE_PREFIX} Please resolve pending discussions, @${recipient}!`,
);
return; // get merge request discussions
} const discussions = await api.getMergeRequestDiscussions(mergeRequest.project_id, mergeRequest.iid);
if (approval.merge_status === MergeRequestMergeStatus.CAN_BE_MERGED) { // check if at least one of the discussions is unresolved
if (approval.approvals_left > 0) { const hasUnresolvedDiscussions = discussions.some(discussion => {
Logger.warn(`Merge request '${mergeRequest.title}' needs more approvals!`); return discussion.notes.some(note => {
return note.resolvable && (typeof note.resolved === 'undefined' || !note.resolved);
// get possible appropers, prefixed with '@' and joined with commas
const possibleApprovers = maintainerUsernames
.filter(username => {
if (mergeRequest.assignee.username === username) {
return false;
}
if (username.includes('openstapps') || username.includes('kphilipp')) {
return false;
}
if (approval.approved_by.length === 0) {
return true;
}
return approval.approved_by.find(
(approver: {
/**
* Possible approver
*/
user: User;
}) => {
return approver.user.username !== username;
},
);
})
.map(username => `@${username}`)
.join(' ');
// send message to slack
await client?.chat.postMessage({
channel: SLACK_CHANNEL,
text: `Merge request '${mergeRequest.title}' needs more approvals! See ${mergeRequest.web_url}!`,
}); });
});
// assign reviewers if (hasUnresolvedDiscussions) {
await api.createNote( let recipient = mergeRequest.author.username;
mergeRequest.project_id,
Scope.MERGE_REQUESTS,
mergeRequest.iid,
`/assign_reviewer ${possibleApprovers}`,
);
} else {
Logger.log(`Merge request '${mergeRequest.title}' is ready to be merged!`);
// send message to slack if (typeof mergeRequest.assignee !== 'undefined' && mergeRequest.assignee !== null) {
await client?.chat.postMessage({ recipient = mergeRequest.assignee.username;
channel: SLACK_CHANNEL, }
text: `Merge request '${mergeRequest.title}' is ready to be merged! See ${mergeRequest.web_url}!`,
});
// prefix maintainers with '@' and join with commas
const possibleMergers = maintainerUsernames
.filter(username => {
return mergeRequest.assignee.username !== username;
})
.map(username => `@${username}`)
.join(', ');
// create note in merge request // create note in merge request
await api.createNote( await api.createNote(
mergeRequest.project_id, mergeRequest.project_id,
Scope.MERGE_REQUESTS, Scope.MERGE_REQUESTS,
mergeRequest.iid, mergeRequest.iid,
`${NOTE_PREFIX} Merge request is ready to be merged, ${possibleMergers}!`, `${NOTE_PREFIX} Please resolve pending discussions, @${recipient}!`,
); );
return;
} }
}
}); if (approval.merge_status === MergeRequestMergeStatus.CAN_BE_MERGED) {
if (approval.approvals_left > 0) {
Logger.warn(`Merge request '${mergeRequest.title}' needs more approvals!`);
// get possible appropers, prefixed with '@' and joined with commas
const possibleApprovers = maintainerUsernames
.filter(username => {
if (mergeRequest.assignee.username === username) {
return false;
}
if (username.includes('openstapps') || username.includes('kphilipp')) {
return false;
}
if (approval.approved_by.length === 0) {
return true;
}
return approval.approved_by.find(
(approver: {
/**
* Possible approver
*/
user: User;
}) => {
return approver.user.username !== username;
},
);
})
.map(username => `@${username}`)
.join(' ');
// send message to slack
await client?.chat.postMessage({
channel: SLACK_CHANNEL,
text: `Merge request '${mergeRequest.title}' needs more approvals! See ${mergeRequest.web_url}!`,
});
// assign reviewers
await api.createNote(
mergeRequest.project_id,
Scope.MERGE_REQUESTS,
mergeRequest.iid,
`/assign_reviewer ${possibleApprovers}`,
);
} else {
Logger.log(`Merge request '${mergeRequest.title}' is ready to be merged!`);
// send message to slack
await client?.chat.postMessage({
channel: SLACK_CHANNEL,
text: `Merge request '${mergeRequest.title}' is ready to be merged! See ${mergeRequest.web_url}!`,
});
// prefix maintainers with '@' and join with commas
const possibleMergers = maintainerUsernames
.filter(username => {
return mergeRequest.assignee.username !== username;
})
.map(username => `@${username}`)
.join(', ');
// create note in merge request
await api.createNote(
mergeRequest.project_id,
Scope.MERGE_REQUESTS,
mergeRequest.iid,
`${NOTE_PREFIX} Merge request is ready to be merged, ${possibleMergers}!`,
);
}
}
}),
);
} }

View File

@@ -12,7 +12,6 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {asyncPool} from '@krlwlfrt/async-pool';
import {Api} from '@openstapps/gitlab-api'; import {Api} from '@openstapps/gitlab-api';
import { import {
Issue, Issue,
@@ -23,12 +22,16 @@ import {
User, User,
} from '@openstapps/gitlab-api/lib/types'; } from '@openstapps/gitlab-api/lib/types';
import {Logger} from '@openstapps/logger'; import {Logger} from '@openstapps/logger';
import moment from 'moment';
import {render} from 'mustache'; import {render} from 'mustache';
import path from 'path'; import path from 'path';
import {cwd} from 'process'; import {cwd} from 'process';
import {flatten2dArray, getProjects, readFilePromisified, writeFilePromisified} from '../common'; import {getProjects} from '../common';
import {BOLD_LABELS, CONCURRENCY, GROUPS, LABEL_WEIGHTS, NEXT_MEETING} from '../configuration'; import {BOLD_LABELS, GROUPS, LABEL_WEIGHTS, NEXT_MEETING} from '../configuration';
import differenceInWeeks from 'date-fns/differenceInWeeks';
import formatISO from 'date-fns/formatISO';
import format from 'date-fns/format';
import de from 'date-fns/locale/de';
import {readFile, writeFile} from 'fs/promises';
/** /**
* A structure for template compilation * A structure for template compilation
@@ -188,13 +191,15 @@ export function getMergeRequestUrls(
* @param groups List of groups to get issues for * @param groups List of groups to get issues for
*/ */
export async function getIssues(api: Api, label: string, groups: number[]): Promise<Issue[]> { export async function getIssues(api: Api, label: string, groups: number[]): Promise<Issue[]> {
const issueResults = await asyncPool(CONCURRENCY, groups, async groupId => { const issueResults = await Promise.all(
return api.getIssues({ groups.map(groupId =>
groupId: groupId, api.getIssues({
}); groupId: groupId,
}); }),
),
);
const issues = flatten2dArray(issueResults).filter(issue => { const issues = issueResults.flat().filter(issue => {
return issue.labels.includes(label); return issue.labels.includes(label);
}); });
@@ -212,21 +217,23 @@ export async function getIssues(api: Api, label: string, groups: number[]): Prom
export async function getIssueBranches(api: Api, projects: Project[]): Promise<{[k: string]: number[]}> { export async function getIssueBranches(api: Api, projects: Project[]): Promise<{[k: string]: number[]}> {
const projectBranches: {[k: string]: number[]} = {}; const projectBranches: {[k: string]: number[]} = {};
await asyncPool(CONCURRENCY, projects, async project => { await Promise.all(
const branches = await api.getBranchesForProject(project.id); projects.map(async project => {
const branches = await api.getBranchesForProject(project.id);
// extract issue number from branch // extract issue number from branch
projectBranches[project.id] = branches projectBranches[project.id] = branches
.map(branch => { .map(branch => {
return branch.name.split('-')[0]; return branch.name.split('-')[0];
}) })
.filter(branchNameStart => { .filter(branchNameStart => {
return branchNameStart.match(/^[0-9]+$/); return branchNameStart.match(/^[0-9]+$/);
}) })
.map(branchNameStart => { .map(branchNameStart => {
return Number.parseInt(branchNameStart, 10); return Number.parseInt(branchNameStart, 10);
}); });
}); }),
);
return projectBranches; return projectBranches;
} }
@@ -240,14 +247,14 @@ export async function getIssueBranches(api: Api, projects: Project[]): Promise<{
export async function getIssuesGroupedByAssignees(api: Api, label: string): Promise<AssigneeWithIssues[]> { export async function getIssuesGroupedByAssignees(api: Api, label: string): Promise<AssigneeWithIssues[]> {
const issuesByAssignee: IssuesGroupedByAssigneeId = {}; const issuesByAssignee: IssuesGroupedByAssigneeId = {};
const groups = flatten2dArray( const groups = await Promise.all(
await asyncPool(CONCURRENCY, GROUPS, async groupId => { GROUPS.map(async groupId => {
const subGroups = await api.getSubGroupsForGroup(groupId); const subGroups = await api.getSubGroupsForGroup(groupId);
return subGroups.map(group => { return subGroups.map(group => {
return group.id; return group.id;
}); });
}), }),
); ).then(it => it.flat());
groups.push(...groups, ...GROUPS); groups.push(...groups, ...GROUPS);
const [issues, projects] = await Promise.all([getIssues(api, label, groups), getProjects(api, groups)]); const [issues, projects] = await Promise.all([getIssues(api, label, groups), getProjects(api, groups)]);
@@ -292,7 +299,7 @@ export async function getIssuesGroupedByAssignees(api: Api, label: string): Prom
}), }),
$mergeRequestUrl: getMergeRequestUrls(mergeRequests, issue.project_id, issue.iid)[0], $mergeRequestUrl: getMergeRequestUrls(mergeRequests, issue.project_id, issue.iid)[0],
$project: issue.web_url.replace('https://gitlab.com/', '').split('/-/issues/')[0], $project: issue.web_url.replace('https://gitlab.com/', '').split('/-/issues/')[0],
$weeksOpen: moment().diff(moment(issue.created_at), 'weeks'), $weeksOpen: differenceInWeeks(Date.now(), new Date(issue.created_at)),
}; };
const issueWithMeta: IssueWithMeta = { const issueWithMeta: IssueWithMeta = {
@@ -370,7 +377,7 @@ export async function getIssuesGroupedByAssignees(api: Api, label: string): Prom
* Get next meeting day * Get next meeting day
*/ */
export function getNextMeetingDay() { export function getNextMeetingDay() {
const meetingDay = NEXT_MEETING.format('YYYY-MM-DD'); const meetingDay = formatISO(NEXT_MEETING, {representation: 'date'});
// log found meeting day // log found meeting day
Logger.info(`Generating report for '${meetingDay}' of '${GROUPS.length}' group(s)...`); Logger.info(`Generating report for '${meetingDay}' of '${GROUPS.length}' group(s)...`);
@@ -388,35 +395,37 @@ export async function getMergeRequests(api: Api, projects: Project[]): Promise<M
const projectMergeRequests: MergeRequestsForProjects = {}; const projectMergeRequests: MergeRequestsForProjects = {};
// iterate over projects // iterate over projects
await asyncPool(CONCURRENCY, projects, async project => { await Promise.all(
// check if project can have merge requests projects.map(async project => {
if (!project.merge_requests_enabled) { // check if project can have merge requests
return; if (!project.merge_requests_enabled) {
} return;
}
// get all merge requests for project // get all merge requests for project
const mergeRequests = await api.getMergeRequests( const mergeRequests = await api.getMergeRequests(
MembershipScope.PROJECTS, MembershipScope.PROJECTS,
project.id, project.id,
MergeRequestState.OPENED, MergeRequestState.OPENED,
); );
// extract issue number from merge request // extract issue number from merge request
projectMergeRequests[project.id] = mergeRequests projectMergeRequests[project.id] = mergeRequests
.map(mergeRequest => { .map(mergeRequest => {
// keep information about web url too // keep information about web url too
return {issue_iid: mergeRequest.source_branch.split('-')[0], web_url: mergeRequest.web_url}; return {issue_iid: mergeRequest.source_branch.split('-')[0], web_url: mergeRequest.web_url};
}) })
.filter(branchNameStartAndUrl => { .filter(branchNameStartAndUrl => {
return branchNameStartAndUrl.issue_iid.match(/^[0-9]+$/); return branchNameStartAndUrl.issue_iid.match(/^[0-9]+$/);
}) })
.map(branchNameStartAndUrl => { .map(branchNameStartAndUrl => {
return { return {
issue_iid: Number.parseInt(branchNameStartAndUrl.issue_iid, 10), issue_iid: Number.parseInt(branchNameStartAndUrl.issue_iid, 10),
web_url: branchNameStartAndUrl.web_url, web_url: branchNameStartAndUrl.web_url,
}; };
}); });
}); }),
);
return projectMergeRequests; return projectMergeRequests;
} }
@@ -434,7 +443,7 @@ export async function generateReport(api: Api, label: string, template: string):
const structureForTemplate: StructureForTemplate = { const structureForTemplate: StructureForTemplate = {
issuesByAssignee: issuesGroupedByAssignee, issuesByAssignee: issuesGroupedByAssignee,
meetingDay: getNextMeetingDay(), meetingDay: getNextMeetingDay(),
timestamp: moment().format('LLL'), timestamp: format(Date.now(), 'PPPPpp', {locale: de}),
}; };
return render(template, structureForTemplate); return render(template, structureForTemplate);
@@ -449,7 +458,7 @@ export async function generateReport(api: Api, label: string, template: string):
export async function report(api: Api, label: string) { export async function report(api: Api, label: string) {
const meetingDay = getNextMeetingDay(); const meetingDay = getNextMeetingDay();
const readReportFile = await readFilePromisified( const readReportFile = await readFile(
// eslint-disable-next-line unicorn/prefer-module // eslint-disable-next-line unicorn/prefer-module
path.resolve(__dirname, '..', '..', 'templates', 'report.md.mustache'), path.resolve(__dirname, '..', '..', 'templates', 'report.md.mustache'),
); );
@@ -462,7 +471,7 @@ export async function report(api: Api, label: string) {
filename = path.join(cwd(), 'reports', `${label}.md`); filename = path.join(cwd(), 'reports', `${label}.md`);
} }
await writeFilePromisified(filename, markdown); await writeFile(filename, markdown);
Logger.ok(`Wrote file '${filename}'.`); Logger.ok(`Wrote file '${filename}'.`);
} }

View File

@@ -12,7 +12,6 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {asyncPool} from '@krlwlfrt/async-pool';
import {Api} from '@openstapps/gitlab-api'; import {Api} from '@openstapps/gitlab-api';
import { import {
AccessLevel, AccessLevel,
@@ -24,9 +23,8 @@ import {
Scope, Scope,
} from '@openstapps/gitlab-api/lib/types'; } from '@openstapps/gitlab-api/lib/types';
import {Logger} from '@openstapps/logger'; import {Logger} from '@openstapps/logger';
import {flatten2dArray, getProjects} from '../common'; import {getProjects} from '../common';
import { import {
CONCURRENCY,
GROUPS, GROUPS,
NEEDED_LABELS, NEEDED_LABELS,
NEEDED_MILESTONES, NEEDED_MILESTONES,
@@ -44,51 +42,52 @@ import {
*/ */
export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> { export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> {
// fetch issues without milestone from all groups // fetch issues without milestone from all groups
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async groupId => { const issuesWithoutMilestone = await Promise.all(
return api.getIssues({ GROUPS.map(groupId =>
groupId: groupId, api.getIssues({
milestone: 'No Milestone', groupId: groupId,
state: IssueState.OPENED, milestone: 'No Milestone',
}); state: IssueState.OPENED,
}); }),
),
// flatten structure, e.g. put all issues in one array ).then(it => it.flat());
const issuesWithoutMilestone = flatten2dArray(issueResults);
Logger.info(`Found '${issuesWithoutMilestone.length}' issue(s) without milestone.`); Logger.info(`Found '${issuesWithoutMilestone.length}' issue(s) without milestone.`);
const milestoneCache: {[s: number]: Milestone[]} = {}; const milestoneCache: {[s: number]: Milestone[]} = {};
await asyncPool(CONCURRENCY, issuesWithoutMilestone, async issue => { await Promise.all(
if (typeof milestoneCache[issue.project_id] === 'undefined') { issuesWithoutMilestone.map(async issue => {
milestoneCache[issue.project_id] = await api.getMilestonesForProject(issue.project_id); if (typeof milestoneCache[issue.project_id] === 'undefined') {
} milestoneCache[issue.project_id] = await api.getMilestonesForProject(issue.project_id);
let milestoneId;
for (const milestone of milestoneCache[issue.project_id]) {
if (milestone.title === 'Meeting') {
milestoneId = milestone.id;
} }
}
if (typeof milestoneId === 'undefined') { let milestoneId;
Logger.warn(`Milestone 'Meeting' was not available for issue ${issue.title} (${issue.web_url}).`);
return; for (const milestone of milestoneCache[issue.project_id]) {
} if (milestone.title === 'Meeting') {
milestoneId = milestone.id;
}
}
await api.setMilestoneForIssue(issue, milestoneId); if (typeof milestoneId === 'undefined') {
Logger.warn(`Milestone 'Meeting' was not available for issue ${issue.title} (${issue.web_url}).`);
Logger.log(`Milestone was set to 'Meeting' for issue ${issue.title} (${issue.web_url})`); return;
}
await api.createNote( await api.setMilestoneForIssue(issue, milestoneId);
issue.project_id,
Scope.ISSUES, Logger.log(`Milestone was set to 'Meeting' for issue ${issue.title} (${issue.web_url})`);
issue.iid,
`${NOTE_PREFIX} Milestone was set automatically to 'Meeting'.`, await api.createNote(
); issue.project_id,
}); Scope.ISSUES,
issue.iid,
`${NOTE_PREFIX} Milestone was set automatically to 'Meeting'.`,
);
}),
);
Logger.ok('Tidied issues without milestones.'); Logger.ok('Tidied issues without milestones.');
} }
@@ -102,15 +101,14 @@ export async function tidyIssuesWithoutMilestone(api: Api): Promise<void> {
*/ */
export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void> { export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void> {
// fetch all open issues // fetch all open issues
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async groupId => { const openIssues = await Promise.all(
return api.getIssues({ GROUPS.map(groupId =>
groupId: groupId, api.getIssues({
state: IssueState.OPENED, groupId: groupId,
}); state: IssueState.OPENED,
}); }),
),
// flatten structure, e.g. put all issues in one array ).then(it => it.flat());
const openIssues = flatten2dArray(issueResults);
Logger.info(`Found ${openIssues.length} open issue(s).`); Logger.info(`Found ${openIssues.length} open issue(s).`);
@@ -121,20 +119,22 @@ export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void>
Logger.info(`Filtered ${openIssuesWithoutMeetingLabel.length} open issue(s) without label 'meeting'.`); Logger.info(`Filtered ${openIssuesWithoutMeetingLabel.length} open issue(s) without label 'meeting'.`);
await asyncPool(CONCURRENCY, openIssuesWithoutMeetingLabel, async issue => { await Promise.all(
if (issue.milestone !== null && issue.milestone.title === 'Backlog') { openIssuesWithoutMeetingLabel.map(async issue => {
Logger.info(`Skipping issue "${issue.title}" because it is in backlog.`); if (issue.milestone !== null && issue.milestone.title === 'Backlog') {
Logger.info(`Skipping issue "${issue.title}" because it is in backlog.`);
return; return;
} }
return api.createNote( return api.createNote(
issue.project_id, issue.project_id,
Scope.ISSUES, Scope.ISSUES,
issue.iid, issue.iid,
`${NOTE_PREFIX} Automatically adding label 'meeting'\n\n/label ~meeting`, `${NOTE_PREFIX} Automatically adding label 'meeting'\n\n/label ~meeting`,
); );
}); }),
);
Logger.ok(`Tidied open issues without label 'meeting'.`); Logger.ok(`Tidied open issues without label 'meeting'.`);
} }
@@ -146,39 +146,43 @@ export async function tidyOpenIssuesWithoutMeetingLabel(api: Api): Promise<void>
* @param projects List of projects to tidy labels on * @param projects List of projects to tidy labels on
*/ */
export async function tidyLabels(api: Api, projects: Project[]): Promise<void> { export async function tidyLabels(api: Api, projects: Project[]): Promise<void> {
await asyncPool(CONCURRENCY, projects, async project => { await Promise.all(
const labels = await api.getLabels(project.id); projects.map(async project => {
const labels = await api.getLabels(project.id);
const neededLabels = [...NEEDED_LABELS]; const neededLabels = [...NEEDED_LABELS];
// const extraneousLabels: Label[] = []; // const extraneousLabels: Label[] = [];
for (const label of labels) { for (const label of labels) {
// let needed = false; // let needed = false;
for (const [neededLabelIndex, neededLabel] of neededLabels.entries()) { for (const [neededLabelIndex, neededLabel] of neededLabels.entries()) {
if (neededLabel.name.toLowerCase() === label.name.toLowerCase()) { if (neededLabel.name.toLowerCase() === label.name.toLowerCase()) {
neededLabels.splice(neededLabelIndex, 1); neededLabels.splice(neededLabelIndex, 1);
// needed = true; // needed = true;
}
} }
}
/* if (!needed) { /* if (!needed) {
extraneousLabels.push(label); extraneousLabels.push(label);
} */ } */
} }
await asyncPool(CONCURRENCY, neededLabels, async neededLabel => { await Promise.all(
await api.createLabel(project.id, neededLabel.name, neededLabel.description, neededLabel.color); neededLabels.map(async neededLabel => {
await api.createLabel(project.id, neededLabel.name, neededLabel.description, neededLabel.color);
Logger.log(`Created label '${neededLabel.name}' in '${project.name_with_namespace}'.`); Logger.log(`Created label '${neededLabel.name}' in '${project.name_with_namespace}'.`);
}); }),
);
// await asyncPool(2, extraneousLabels, async (extraneousLabel) => { // await asyncPool(2, extraneousLabels, async (extraneousLabel) => {
// await api.deleteLabel(project.id, extraneousLabel.name); // await api.deleteLabel(project.id, extraneousLabel.name);
// //
// Logger.log('Deleted label `' + extraneousLabel.name + '` from ' + project.name_with_namespace + '.'); // Logger.log('Deleted label `' + extraneousLabel.name + '` from ' + project.name_with_namespace + '.');
// }); // });
}); }),
);
Logger.ok('Tidied labels.'); Logger.ok('Tidied labels.');
} }
@@ -190,25 +194,29 @@ export async function tidyLabels(api: Api, projects: Project[]): Promise<void> {
* @param projects List of projects to tidy milestones on * @param projects List of projects to tidy milestones on
*/ */
export async function tidyMilestones(api: Api, projects: Project[]): Promise<void> { export async function tidyMilestones(api: Api, projects: Project[]): Promise<void> {
await asyncPool(CONCURRENCY, projects, async project => { await Promise.all(
const milestones = await api.getMilestonesForProject(project.id); projects.map(async project => {
const missingMilestones = [...NEEDED_MILESTONES]; const milestones = await api.getMilestonesForProject(project.id);
const missingMilestones = [...NEEDED_MILESTONES];
for (const milestone of milestones) { for (const milestone of milestones) {
const index = missingMilestones.indexOf(milestone.title); const index = missingMilestones.indexOf(milestone.title);
if (index >= 0) { if (index >= 0) {
missingMilestones.splice(index, 1); missingMilestones.splice(index, 1);
}
} }
}
if (missingMilestones.length > 0 && !project.archived) { if (missingMilestones.length > 0 && !project.archived) {
await asyncPool(CONCURRENCY, missingMilestones, async milestone => { await Promise.all(
await api.createMilestone(project.id, milestone); missingMilestones.map(async milestone => {
Logger.log(`Created milestone '${milestone}' for project ${project.name_with_namespace}'.`); await api.createMilestone(project.id, milestone);
}); Logger.log(`Created milestone '${milestone}' for project ${project.name_with_namespace}'.`);
} }),
}); );
}
}),
);
Logger.ok('Tidied milestones.'); Logger.ok('Tidied milestones.');
} }
@@ -220,23 +228,29 @@ export async function tidyMilestones(api: Api, projects: Project[]): Promise<voi
* @param projects List of projects to tidy milestones on * @param projects List of projects to tidy milestones on
*/ */
export async function tidyProtectedBranches(api: Api, projects: Project[]): Promise<void> { export async function tidyProtectedBranches(api: Api, projects: Project[]): Promise<void> {
await asyncPool(CONCURRENCY, projects, async project => { await Promise.all(
const branches = await api.getBranchesForProject(project.id); projects.map(async project => {
const branches = await api.getBranchesForProject(project.id);
const protectableBranches = branches.filter(branch => { const protectableBranches = branches.filter(branch => {
return PROTECTED_BRANCHES.includes(branch.name); return PROTECTED_BRANCHES.includes(branch.name);
}); });
const unprotectedBranches = protectableBranches.filter(branch => { const unprotectedBranches = protectableBranches.filter(branch => {
return !branch.protected; return !branch.protected;
}); });
await asyncPool(CONCURRENCY, unprotectedBranches, async branch => { await Promise.all(
await api.protectBranch(project.id, branch.name); unprotectedBranches.map(async branch => {
await api.protectBranch(project.id, branch.name);
Logger.log(`Added protected branch '${branch.name}' in project '${project.name_with_namespace}'...`); Logger.log(
}); `Added protected branch '${branch.name}' in project '${project.name_with_namespace}'...`,
}); );
}),
);
}),
);
Logger.ok('Tidied protected branches.'); Logger.ok('Tidied protected branches.');
} }
@@ -248,44 +262,48 @@ export async function tidyProtectedBranches(api: Api, projects: Project[]): Prom
* @param projects List of projects to tidy protected tags on * @param projects List of projects to tidy protected tags on
*/ */
export async function tidyProtectedTags(api: Api, projects: Project[]): Promise<void> { export async function tidyProtectedTags(api: Api, projects: Project[]): Promise<void> {
await asyncPool(CONCURRENCY, projects, async project => { await Promise.all(
// TODO: move this to GitLab API projects.map(async project => {
const protectedTags = (await api.makeGitLabAPIRequest(`projects/${project.id}/protected_tags`)) as Array<{ // TODO: move this to GitLab API
/** const protectedTags = (await api.makeGitLabAPIRequest(
* List of access levels to create a tag `projects/${project.id}/protected_tags`,
*/ )) as Array<{
create_access_levels: Array<{
/** /**
* Access level * List of access levels to create a tag
*/ */
access_level: AccessLevel; create_access_levels: Array<{
/**
* Access level
*/
access_level: AccessLevel;
/**
* Description of access level
*/
access_level_description: string;
}>;
/** /**
* Description of access level * Name of the tag
*/ */
access_level_description: string; name: string;
}>; }>;
/**
* Name of the tag
*/
name: string;
}>;
if ( if (
protectedTags.findIndex(protectedTag => { protectedTags.findIndex(protectedTag => {
return protectedTag.name === 'v*'; return protectedTag.name === 'v*';
}) === -1 }) === -1
) { ) {
await api.makeGitLabAPIRequest(`projects/${project.id}/protected_tags`, { await api.makeGitLabAPIRequest(`projects/${project.id}/protected_tags`, {
data: { data: {
create_access_level: AccessLevel.Maintainer, create_access_level: AccessLevel.Maintainer,
name: 'v*', name: 'v*',
}, },
method: 'POST', method: 'POST',
}); });
Logger.log(`Added protected version tag in project '${project.name_with_namespace}'.`); Logger.log(`Added protected version tag in project '${project.name_with_namespace}'.`);
} }
}); }),
);
Logger.ok('Tidied protected tags.'); Logger.ok('Tidied protected tags.');
} }
@@ -305,26 +323,32 @@ export async function tidySubGroupMembers(api: Api): Promise<void> {
groupIdsToSchool[SCHOOLS[school]] = school; groupIdsToSchool[SCHOOLS[school]] = school;
}); });
await asyncPool(CONCURRENCY, GROUPS.slice(1), async groupId => { await Promise.all(
const members = await api.getMembers(MembershipScope.GROUPS, groupId); GROUPS.slice(1).map(async groupId => {
const memberIds = new Set(members.map(member => member.id)); const members = await api.getMembers(MembershipScope.GROUPS, groupId);
const memberIds = new Set(members.map(member => member.id));
await asyncPool(CONCURRENCY, stappsMembers, async stappsMember => { await Promise.all(
if (!memberIds.has(stappsMember.id)) { stappsMembers.map(async stappsMember => {
await api.addMember(MembershipScope.GROUPS, groupId, stappsMember.id, AccessLevel.Developer); if (!memberIds.has(stappsMember.id)) {
await api.addMember(MembershipScope.GROUPS, groupId, stappsMember.id, AccessLevel.Developer);
Logger.log(`Added '${stappsMember.name}' to group '${groupIdsToSchool[groupId]}'.`); Logger.log(`Added '${stappsMember.name}' to group '${groupIdsToSchool[groupId]}'.`);
} }
}); }),
);
await asyncPool(CONCURRENCY, members, async member => { await Promise.all(
if (!stappsMemberIds.has(member.id)) { members.map(async member => {
await api.deleteMember(MembershipScope.GROUPS, groupId, member.id); if (!stappsMemberIds.has(member.id)) {
await api.deleteMember(MembershipScope.GROUPS, groupId, member.id);
Logger.log(`Deleted member '${member.name}' from group '${groupIdsToSchool[groupId]}'.`); Logger.log(`Deleted member '${member.name}' from group '${groupIdsToSchool[groupId]}'.`);
} }
}); }),
}); );
}),
);
Logger.ok(`Tidied 'sub' group members.`); Logger.ok(`Tidied 'sub' group members.`);
} }
@@ -338,15 +362,14 @@ export async function tidySubGroupMembers(api: Api): Promise<void> {
*/ */
export async function tidyIssuesWithoutAssignee(api: Api): Promise<void> { export async function tidyIssuesWithoutAssignee(api: Api): Promise<void> {
// fetch issues without milestone from all groups // fetch issues without milestone from all groups
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async groupId => { const issues = await Promise.all(
return api.getIssues({ GROUPS.map(async groupId => {
groupId: groupId, return api.getIssues({
state: IssueState.OPENED, groupId: groupId,
}); state: IssueState.OPENED,
}); });
}),
// flatten structure, e.g. put all issues in one array ).then(it => it.flat());
const issues = flatten2dArray(issueResults);
const issuesWithoutAssignee = issues.filter(issue => { const issuesWithoutAssignee = issues.filter(issue => {
return issue.assignee === null; return issue.assignee === null;
@@ -354,18 +377,20 @@ export async function tidyIssuesWithoutAssignee(api: Api): Promise<void> {
Logger.info(`Found '${issuesWithoutAssignee.length}' issue(s) without assignee.`); Logger.info(`Found '${issuesWithoutAssignee.length}' issue(s) without assignee.`);
await asyncPool(CONCURRENCY, issuesWithoutAssignee, async issue => { await Promise.all(
await api.setAssigneeForIssue(issue, issue.author.id); issuesWithoutAssignee.map(async issue => {
await api.setAssigneeForIssue(issue, issue.author.id);
Logger.log(`Set assignee for '${issue.title}' to '${issue.author.name}'.`); Logger.log(`Set assignee for '${issue.title}' to '${issue.author.name}'.`);
await api.createNote( await api.createNote(
issue.project_id, issue.project_id,
Scope.ISSUES, Scope.ISSUES,
issue.iid, issue.iid,
`${NOTE_PREFIX} Assignee was set automatically to author.`, `${NOTE_PREFIX} Assignee was set automatically to author.`,
); );
}); }),
);
Logger.ok('Tidied issues without assignee.'); Logger.ok('Tidied issues without assignee.');
} }
@@ -378,12 +403,11 @@ export async function tidyIssuesWithoutAssignee(api: Api): Promise<void> {
* @param api GitLab API instance to use for the requests * @param api GitLab API instance to use for the requests
*/ */
export async function tidyMergeRequestsWithoutAssignee(api: Api): Promise<void> { export async function tidyMergeRequestsWithoutAssignee(api: Api): Promise<void> {
const mergeRequestResults = await asyncPool(CONCURRENCY, GROUPS, async groupId => { const mergeRequests = await Promise.all(
return api.getMergeRequests(MembershipScope.GROUPS, groupId, MergeRequestState.OPENED); GROUPS.map(async groupId => {
}); return api.getMergeRequests(MembershipScope.GROUPS, groupId, MergeRequestState.OPENED);
}),
// flatten structure, e.g. put all issues in one array ).then(it => it.flat());
const mergeRequests = flatten2dArray(mergeRequestResults);
const mergeRequestsWithoutAssignee = mergeRequests.filter(mergeRequest => { const mergeRequestsWithoutAssignee = mergeRequests.filter(mergeRequest => {
return mergeRequest.assignee === null; return mergeRequest.assignee === null;
@@ -391,18 +415,20 @@ export async function tidyMergeRequestsWithoutAssignee(api: Api): Promise<void>
Logger.info(`Found '${mergeRequestsWithoutAssignee.length}' merge requests without assignee.`); Logger.info(`Found '${mergeRequestsWithoutAssignee.length}' merge requests without assignee.`);
await asyncPool(CONCURRENCY, mergeRequestsWithoutAssignee, async mergeRequest => { await Promise.all(
await api.setAssigneeForMergeRequest(mergeRequest, mergeRequest.author.id); mergeRequestsWithoutAssignee.map(async mergeRequest => {
await api.setAssigneeForMergeRequest(mergeRequest, mergeRequest.author.id);
Logger.log(`Set assignee for '${mergeRequest.title}' to '${mergeRequest.author.name}'.`); Logger.log(`Set assignee for '${mergeRequest.title}' to '${mergeRequest.author.name}'.`);
await api.createNote( await api.createNote(
mergeRequest.project_id, mergeRequest.project_id,
Scope.MERGE_REQUESTS, Scope.MERGE_REQUESTS,
mergeRequest.iid, mergeRequest.iid,
`${NOTE_PREFIX} Assignee was set automatically to author.`, `${NOTE_PREFIX} Assignee was set automatically to author.`,
); );
}); }),
);
Logger.ok('Tidied merge requests without assignee.'); Logger.ok('Tidied merge requests without assignee.');
} }

View File

@@ -12,13 +12,11 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {asyncPool} from '@krlwlfrt/async-pool';
import {Api} from '@openstapps/gitlab-api'; import {Api} from '@openstapps/gitlab-api';
import {IssueState, Scope} from '@openstapps/gitlab-api/lib/types'; import {IssueState, Scope} from '@openstapps/gitlab-api/lib/types';
import {Logger} from '@openstapps/logger'; import {Logger} from '@openstapps/logger';
import moment from 'moment'; import {GROUPS, LAST_MEETING, NOTE_PREFIX} from '../configuration';
import {flatten2dArray} from '../common'; import isBefore from 'date-fns/isBefore';
import {CONCURRENCY, GROUPS, LAST_MEETING, NOTE_PREFIX} from '../configuration';
/** /**
* Remove label `meeting` from closed issues * Remove label `meeting` from closed issues
@@ -26,34 +24,38 @@ import {CONCURRENCY, GROUPS, LAST_MEETING, NOTE_PREFIX} from '../configuration';
* @param api Instance of GitLabAPI to send requests with * @param api Instance of GitLabAPI to send requests with
*/ */
export async function unlabel(api: Api) { export async function unlabel(api: Api) {
const issueResults = await asyncPool(CONCURRENCY, GROUPS, async groupId => { const issues = await Promise.all(
return api.getIssues({ GROUPS.map(async groupId => {
groupId: groupId, return api.getIssues({
state: IssueState.CLOSED, groupId: groupId,
}); state: IssueState.CLOSED,
}); });
}),
const issues = flatten2dArray(issueResults); ).then(it => it.flat());
Logger.log(`Fetched ${issues.length} closed issue(s).`); Logger.log(`Fetched ${issues.length} closed issue(s).`);
await asyncPool(CONCURRENCY, issues, async issue => { await Promise.all(
if ( issues.map(async issue => {
issue.labels.includes('meeting') && if (
issue.closed_at !== null && issue.labels.includes('meeting') &&
moment(issue.closed_at).isBefore(LAST_MEETING) issue.closed_at !== null &&
) { isBefore(new Date(issue.closed_at), LAST_MEETING)
Logger.info(`Issue ${issue.title} is closed before last meeting and has label "meeting". Removing it.`); ) {
Logger.info(
`Issue ${issue.title} is closed before last meeting and has label "meeting". Removing it.`,
);
await api.createNote( await api.createNote(
issue.project_id, issue.project_id,
Scope.ISSUES, Scope.ISSUES,
issue.iid, issue.iid,
`${NOTE_PREFIX} Removed label \`meeting\` automatically. `${NOTE_PREFIX} Removed label \`meeting\` automatically.
/unlabel ~meeting`, /unlabel ~meeting`,
); );
} }
}); }),
);
Logger.ok('Label `meeting` has been removed from closed issues.'); Logger.ok('Label `meeting` has been removed from closed issues.');
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,60 +1,55 @@
{ {
"name": "@openstapps/minimal-connector", "name": "@openstapps/minimal-connector",
"version": "0.2.0",
"description": "This is a minimal connector which serves as an example", "description": "This is a minimal connector which serves as an example",
"repository": { "version": "2.1.0",
"type": "git",
"url": "git@gitlab.com:openstapps/minimal-connector.git"
},
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "git@gitlab.com:openstapps/minimal-connector.git",
"author": "Anselm Stordeur <anselmstordeur@gmail.com>", "author": "Anselm Stordeur <anselmstordeur@gmail.com>",
"contributors": [ "contributors": [
"Jovan Krunić <jovan.krunic@gmail.com>", "Jovan Krunić <jovan.krunic@gmail.com>",
"Karl-Philipp Wulfert <krlwlfrt@gmail.com>", "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"Rainer Killinger <git@killinger.co>", "Michel Jonathan Schmitz <michel.schmitz1992@gmail.com>",
"Michel Jonathan Schmitz <michel.schmitz1992@gmail.com>" "Rainer Killinger <git@killinger.co>"
], ],
"main": "lib/cli.js", "main": "lib/cli.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
"scripts": { "scripts": {
"build": "npm run tslint && npm run compile", "build": "npm run compile",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"check-configuration": "openstapps-configuration", "check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'", "compile": "rimraf lib && tsc",
"documentation": "typedoc --includeDeclarations --mode modules --out docs --readme README.md --listInvalidSymbolLinks src", "documentation": "typedoc --includeDeclarations --mode modules --out docs --readme README.md --listInvalidSymbolLinks src",
"lint": "tslint -p tsconfig.json -c tslint.json 'src/**/*.ts'",
"postversion": "npm run changelog", "postversion": "npm run changelog",
"prepublishOnly": "npm ci && npm run build", "prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly", "preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"", "push": "git push && git push origin \"v$npm_package_version\"",
"test": "nyc mocha --require ts-node/register --require source-map-support/register --recursive 'test/*.spec.ts'", "test": "nyc mocha --require ts-node/register --require source-map-support/register --recursive 'test/*.spec.ts'"
"tslint": "tslint -p tsconfig.json -c tslint.json 'src/**/*.ts'"
}, },
"dependencies": { "dependencies": {
"@openstapps/api": "0.28.0", "@openstapps/api": "workspace:*",
"@openstapps/core": "0.46.0", "@openstapps/core": "workspace:*",
"@openstapps/logger": "0.7.0", "@openstapps/logger": "workspace:*",
"commander": "6.2.1" "commander": "10.0.0"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.27.0", "@openstapps/configuration": "workspace:*",
"@openstapps/core-tools": "0.21.0", "@openstapps/core-tools": "workspace:*",
"@testdeck/mocha": "0.1.2", "@testdeck/mocha": "0.3.3",
"@types/chai": "4.2.18", "@types/chai": "4.3.4",
"@types/chai-as-promised": "7.1.4", "@types/chai-as-promised": "7.1.5",
"@types/mocha": "8.2.2", "@types/mocha": "10.0.1",
"@types/node": "14.17.1", "@types/node": "18.15.3",
"chai": "4.3.4", "chai": "4.3.7",
"chai-as-promised": "7.1.1", "chai-as-promised": "7.1.1",
"conventional-changelog-cli": "2.1.1", "conventional-changelog-cli": "2.2.2",
"mocha": "8.4.0", "mocha": "10.2.0",
"nock": "13.1.0", "nock": "13.3.0",
"nyc": "15.1.0", "nyc": "15.1.0",
"prepend-file-cli": "1.0.6", "rimraf": "4.4.0",
"rimraf": "3.0.2", "ts-node": "10.9.1",
"ts-node": "9.1.1", "typedoc": "0.22.18",
"tslint": "6.1.3", "typescript": "4.4.4"
"typedoc": "0.18.0",
"typescript": "3.8.3"
}, },
"nyc": { "nyc": {
"all": true, "all": true,

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/* /*
* Copyright (C) 2018, 2019 StApps * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it

View File

@@ -49,7 +49,7 @@ export abstract class Connector<T extends SCThings> {
* *
* Implementation according to your schools requirements * Implementation according to your schools requirements
*/ */
protected abstract async fetchItems(): Promise<T[]>; protected abstract fetchItems(): Promise<T[]>;
/** /**
* Creates a remote origin with the current date-time * Creates a remote origin with the current date-time

View File

@@ -53,6 +53,7 @@ export class MinimalConnector extends Connector<SCMessage> {
const importedItems: SCMessage[] = [ const importedItems: SCMessage[] = [
{ {
audiences: ['students', 'employees'], audiences: ['students', 'employees'],
categories: [],
description: 'Some description 1', description: 'Some description 1',
messageBody: 'Some message 1', messageBody: 'Some message 1',
name: 'Some name 1', name: 'Some name 1',
@@ -62,6 +63,7 @@ export class MinimalConnector extends Connector<SCMessage> {
}, },
{ {
audiences: ['students', 'employees'], audiences: ['students', 'employees'],
categories: [],
description: 'Some description 2', description: 'Some description 2',
messageBody: 'Some message 2', messageBody: 'Some message 2',
name: 'Some name 2', name: 'Some name 2',
@@ -71,6 +73,7 @@ export class MinimalConnector extends Connector<SCMessage> {
}, },
{ {
audiences: ['students', 'employees'], audiences: ['students', 'employees'],
categories: [],
description: 'Some description 3', description: 'Some description 3',
messageBody: 'Some message 3', messageBody: 'Some message 3',
name: 'Some name 3', name: 'Some name 3',

View File

@@ -1,3 +0,0 @@
{
"extends": "./node_modules/@openstapps/configuration/tslint.json"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +1,42 @@
{ {
"name": "@openstapps/minimal-plugin", "name": "@openstapps/minimal-plugin",
"version": "0.0.1",
"description": "Minimal Plugin", "description": "Minimal Plugin",
"version": "2.1.0",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"author": "Wieland Schöbl", "author": "Thea Schöbl",
"contributors": [ "contributors": [
"Jovan Krunić <jovan.krunic@gmail.com>", "Jovan Krunić <jovan.krunic@gmail.com>",
"Rainer Killinger <mail-openstapps@killinger.co>" "Rainer Killinger <mail-openstapps@killinger.co>"
], ],
"scripts": { "scripts": {
"build": "npm run tslint && npm run compile", "build": "npm run compile",
"compile": "rimraf lib && tsc && prepend lib/cli.js '#!/usr/bin/env node\n'",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'",
"check-configuration": "openstapps-configuration", "check-configuration": "openstapps-configuration",
"compile": "rimraf lib && tsc",
"documentation": "typedoc --includeVersion --out docs src", "documentation": "typedoc --includeVersion --out docs src",
"start": "node lib/cli.js", "lint": "tslint -p tsconfig.json -c tslint.json 'src/**/*.ts'",
"tslint": "tslint -p tsconfig.json -c tslint.json 'src/**/*.ts'",
"prepublishOnly": "npm ci && npm run build",
"postversion": "npm run changelog", "postversion": "npm run changelog",
"prepublishOnly": "npm ci && npm run build",
"preversion": "npm run prepublishOnly", "preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"" "push": "git push && git push origin \"v$npm_package_version\"",
"start": "node lib/cli.js"
}, },
"dependencies": { "dependencies": {
"@openstapps/api": "0.33.0", "@openstapps/api": "workspace:*",
"@openstapps/core": "0.52.0", "@openstapps/core": "workspace:*",
"@openstapps/core-tools": "0.25.0", "@openstapps/core-tools": "workspace:*",
"@openstapps/logger": "0.7.0", "@openstapps/logger": "workspace:*",
"commander": "8.2.0", "commander": "10.0.0",
"express": "4.17.1", "express": "4.18.2",
"ts-node": "10.2.1" "ts-node": "10.9.1"
}, },
"devDependencies": { "devDependencies": {
"@openstapps/configuration": "0.28.1", "@openstapps/configuration": "workspace:*",
"@types/express": "4.17.13", "@types/express": "4.17.16",
"@types/node": "14.17.12", "@types/node": "18.15.3",
"conventional-changelog-cli": "2.1.1", "conventional-changelog-cli": "2.2.2",
"prepend-file-cli": "1.0.6", "rimraf": "4.4.0",
"rimraf": "3.0.2", "typedoc": "0.22.18",
"tslint": "6.1.3", "typescript": "4.4.4"
"typedoc": "0.22.4",
"typescript": "4.4.3"
} }
} }

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/* /*
* Copyright (C) 2019-2021 StApps * Copyright (C) 2019-2021 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it

View File

@@ -1,3 +1,3 @@
{ {
"extends": "./node_modules/@openstapps/configuration/tsconfig.json" "extends": "../../../node_modules/@openstapps/configuration/tsconfig.json"
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "@openstapps/app", "name": "@openstapps/app",
"version": "2.1.1",
"description": "The generic app tailored to fulfill needs of German universities, written using Ionic Framework.", "description": "The generic app tailored to fulfill needs of German universities, written using Ionic Framework.",
"version": "2.1.0",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>", "author": "Karl-Philipp Wulfert <krlwlfrt@gmail.com>",
"contributors": [ "contributors": [
@@ -21,6 +21,7 @@
"build:stats": "ng build --configuration=production --stats-json", "build:stats": "ng build --configuration=production --stats-json",
"changelog": "conventional-changelog -p angular -i src/assets/about/CHANGELOG.md -s -r 0", "changelog": "conventional-changelog -p angular -i src/assets/about/CHANGELOG.md -s -r 0",
"check-configuration": "openstapps-configuration", "check-configuration": "openstapps-configuration",
"check-icons": "ts-node scripts/check-icon-correctness.ts",
"cypress:open": "cypress open", "cypress:open": "cypress open",
"cypress:run": "cypress run", "cypress:run": "cypress run",
"docker:build": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash -c \"npm install && npm run build\"", "docker:build": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash -c \"npm install && npm run build\"",
@@ -31,16 +32,14 @@
"docker:serve": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash -c \"npm run start:external\"", "docker:serve": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash -c \"npm run start:external\"",
"documentation": "compodoc -p tsconfig.json -d docs", "documentation": "compodoc -p tsconfig.json -d docs",
"e2e": "ng e2e", "e2e": "ng e2e",
"licenses": "license-checker --json > src/assets/about/licenses.json && ts-node ./scripts/accumulate-licenses.ts",
"minify-icons": "ts-node scripts/minify-icon-font.ts",
"check-icons": "ts-node scripts/check-icon-correctness.ts",
"format:check": "prettier --check .", "format:check": "prettier --check .",
"format:fix": "prettier --write .", "format:fix": "prettier --write .",
"licenses": "license-checker --json > src/assets/about/licenses.json && ts-node ./scripts/accumulate-licenses.ts && git add src/assets/about/licenses.json",
"lint": "ng lint", "lint": "ng lint",
"lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts,.html src/", "lint:fix": "eslint --fix -c .eslintrc.json --ignore-path .eslintignore --ext .ts,.html src/",
"minify-icons": "ts-node scripts/minify-icon-font.ts",
"ng": "ng", "ng": "ng",
"postinstall": "npx jetify", "postinstall": "npx jetify",
"version": "npm run changelog && npm run licenses && npm run format:fix && git add src/assets/about/CHANGELOG.md && git add src/assets/about/licenses.json",
"prepublishOnly": "npm ci && npm run build && npm run lint && npm run format:check", "prepublishOnly": "npm ci && npm run build && npm run lint && npm run format:check",
"preversion": "npm run prepublishOnly", "preversion": "npm run prepublishOnly",
"push": "git push && git push origin \"v$npm_package_version\"", "push": "git push && git push origin \"v$npm_package_version\"",
@@ -50,7 +49,8 @@
"start": "ionic serve", "start": "ionic serve",
"start:external": "ionic serve --external", "start:external": "ionic serve --external",
"start:prod": "ionic serve -- --configuration=production", "start:prod": "ionic serve -- --configuration=production",
"test": "ng test" "test": "ng test",
"version": "npm run changelog && npm run licenses && npm run format:fix"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "13.3.11", "@angular/animations": "13.3.11",
@@ -83,16 +83,18 @@
"@hugotomazi/capacitor-navigation-bar": "2.0.0", "@hugotomazi/capacitor-navigation-bar": "2.0.0",
"@ionic-native/core": "5.36.0", "@ionic-native/core": "5.36.0",
"@ionic/angular": "6.3.9", "@ionic/angular": "6.3.9",
"@ionic/core": "6.6.1",
"@ionic/storage-angular": "3.0.6", "@ionic/storage-angular": "3.0.6",
"@ngx-translate/core": "14.0.0", "@ngx-translate/core": "14.0.0",
"@ngx-translate/http-loader": "7.0.0", "@ngx-translate/http-loader": "7.0.0",
"@openstapps/api": "1.0.1", "@openid/appauth": "1.3.1",
"@openstapps/configuration": "0.34.0", "@openstapps/api": "workspace:*",
"@openstapps/core": "1.0.1", "@openstapps/configuration": "workspace:*",
"@openstapps/core": "workspace:*",
"@transistorsoft/capacitor-background-fetch": "1.0.2", "@transistorsoft/capacitor-background-fetch": "1.0.2",
"capacitor-secure-storage-plugin": "0.8.1", "capacitor-secure-storage-plugin": "0.8.1",
"cordova-plugin-calendar": "5.1.6", "cordova-plugin-calendar": "5.1.6",
"deepmerge": "4.2.2", "deepmerge": "4.3.0",
"form-data": "4.0.0", "form-data": "4.0.0",
"geojson": "0.5.0", "geojson": "0.5.0",
"ionic-appauth": "0.9.0", "ionic-appauth": "0.9.0",
@@ -131,27 +133,28 @@
"@cypress/schematic": "1.7.0", "@cypress/schematic": "1.7.0",
"@ionic/angular-toolkit": "6.1.0", "@ionic/angular-toolkit": "6.1.0",
"@ionic/cli": "6.20.4", "@ionic/cli": "6.20.4",
"@openstapps/prettier-config": "1.0.0", "@openstapps/prettier-config": "workspace:*",
"@types/fontkit": "1.8.0", "@types/fontkit": "1.8.0",
"@types/glob": "7.2.0", "@types/geojson": "1.0.6",
"@types/glob": "8.0.1",
"@types/jasmine": "4.3.1", "@types/jasmine": "4.3.1",
"@types/jasminewd2": "2.0.10", "@types/jasminewd2": "2.0.10",
"@types/jsonpath": "0.2.0", "@types/jsonpath": "0.2.0",
"@types/leaflet": "1.9.0", "@types/leaflet": "1.9.0",
"@types/leaflet.markercluster": "1.5.1", "@types/leaflet.markercluster": "1.5.1",
"@types/node": "14.18.24", "@types/node": "18.15.3",
"@typescript-eslint/eslint-plugin": "5.45.1", "@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.45.1", "@typescript-eslint/parser": "5.49.0",
"conventional-changelog-cli": "2.2.2", "conventional-changelog-cli": "2.2.2",
"cordova-res": "0.15.4", "cordova-res": "0.15.4",
"cypress": "12.0.1", "cypress": "12.0.1",
"eslint": "8.29.0", "eslint": "8.33.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.6.0",
"eslint-plugin-jsdoc": "39.6.4", "eslint-plugin-jsdoc": "39.7.4",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-unicorn": "43.0.2", "eslint-plugin-unicorn": "45.0.2",
"fontkit": "2.0.2", "fontkit": "2.0.2",
"glob": "8.0.3", "glob": "8.1.0",
"is-docker": "2.2.1", "is-docker": "2.2.1",
"jasmine-core": "4.5.0", "jasmine-core": "4.5.0",
"jasmine-spec-reporter": "7.0.0", "jasmine-spec-reporter": "7.0.0",
@@ -163,17 +166,13 @@
"karma-jasmine-html-reporter": "2.0.0", "karma-jasmine-html-reporter": "2.0.0",
"karma-mocha-reporter": "2.2.5", "karma-mocha-reporter": "2.2.5",
"license-checker": "25.0.1", "license-checker": "25.0.1",
"prettier": "2.7.1", "prettier": "2.8.3",
"protractor": "7.0.0", "protractor": "7.0.0",
"surge": "0.23.1", "surge": "0.23.1",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"typescript": "4.4.4", "typescript": "4.4.4",
"webpack-bundle-analyzer": "4.7.0" "webpack-bundle-analyzer": "4.7.0"
}, },
"engines": {
"node": "^14.20.0",
"npm": "^6.14.17"
},
"cordova": { "cordova": {
"plugins": {}, "plugins": {},
"platforms": [ "platforms": [

View File

@@ -130,7 +130,7 @@ export class AppComponent implements AfterContentInit {
} }
private async showMessage(message?: string) { private async showMessage(message?: string) {
if (typeof message === 'undefined') { if (message === undefined) {
return; return;
} }
const toast = await this.toastController.create({ const toast = await this.toastController.create({

View File

@@ -58,12 +58,14 @@ export class AuthHelperService {
public getAuthMessage(provider: SCAuthorizationProviderType, action: IAuthAction | IPAIAAuthAction) { public getAuthMessage(provider: SCAuthorizationProviderType, action: IAuthAction | IPAIAAuthAction) {
let message: string | undefined; let message: string | undefined;
switch (action.action) { switch (action.action) {
case AuthActions.SignInSuccess: case AuthActions.SignInSuccess: {
message = this.translateService.instant(`auth.messages.${provider}.logged_in_success`); message = this.translateService.instant(`auth.messages.${provider}.logged_in_success`);
break; break;
case AuthActions.SignOutSuccess: }
case AuthActions.SignOutSuccess: {
message = this.translateService.instant(`auth.messages.${provider}.logged_out_success`); message = this.translateService.instant(`auth.messages.${provider}.logged_out_success`);
break; break;
}
} }
return message; return message;
} }

View File

@@ -166,10 +166,10 @@ export abstract class AuthService implements IAuthService {
}); });
} }
if (this._configuration != undefined) { if (this._configuration == undefined) {
return Promise.resolve(this._configuration);
} else {
throw new Error('Unable To Obtain Server Configuration'); throw new Error('Unable To Obtain Server Configuration');
} else {
return Promise.resolve(this._configuration);
} }
} }
@@ -186,36 +186,43 @@ export abstract class AuthService implements IAuthService {
case AuthActions.RefreshFailed: case AuthActions.RefreshFailed:
case AuthActions.SignInFailed: case AuthActions.SignInFailed:
case AuthActions.SignOutSuccess: case AuthActions.SignOutSuccess:
case AuthActions.SignOutFailed: case AuthActions.SignOutFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
break; break;
case AuthActions.LoadTokenFromStorageFailed: }
case AuthActions.LoadTokenFromStorageFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
this._initComplete.next(true); this._initComplete.next(true);
break; break;
}
case AuthActions.SignInSuccess: case AuthActions.SignInSuccess:
case AuthActions.RefreshSuccess: case AuthActions.RefreshSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next(true); this._authenticatedSubject.next(true);
break; break;
case AuthActions.LoadTokenFromStorageSuccess: }
case AuthActions.LoadTokenFromStorageSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0)); this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
this._initComplete.next(true); this._initComplete.next(true);
break; break;
case AuthActions.RevokeTokensSuccess: }
case AuthActions.RevokeTokensSuccess: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
break; break;
case AuthActions.LoadUserInfoSuccess: }
case AuthActions.LoadUserInfoSuccess: {
this._userSubject.next(action.user); this._userSubject.next(action.user);
break; break;
case AuthActions.LoadUserInfoFailed: }
case AuthActions.LoadUserInfoFailed: {
this._userSubject.next(undefined); this._userSubject.next(undefined);
break; break;
}
} }
this._authSubjectV2.next(action); this._authSubjectV2.next(action);
@@ -240,10 +247,10 @@ export abstract class AuthService implements IAuthService {
if (response != undefined) { if (response != undefined) {
this.requestAccessToken(response.code, codeVerifier); this.requestAccessToken(response.code, codeVerifier);
} else if (error != undefined) { } else if (error == undefined) {
throw new Error(error.errorDescription);
} else {
throw new Error('Unknown Error With Authentication'); throw new Error('Unknown Error With Authentication');
} else {
throw new Error(error.errorDescription);
} }
} }
@@ -260,7 +267,10 @@ export abstract class AuthService implements IAuthService {
} }
protected async performEndSessionRequest(state?: string): Promise<void> { protected async performEndSessionRequest(state?: string): Promise<void> {
if (this._tokenSubject.value != undefined) { if (this._tokenSubject.value == undefined) {
//if user has no token they should not be logged in in the first place
this.endSessionCallback();
} else {
const requestJson: EndSessionRequestJson = { const requestJson: EndSessionRequestJson = {
postLogoutRedirectURI: this.authConfig.end_session_redirect_url, postLogoutRedirectURI: this.authConfig.end_session_redirect_url,
idTokenHint: this._tokenSubject.value.idToken || '', idTokenHint: this._tokenSubject.value.idToken || '',
@@ -277,9 +287,6 @@ export abstract class AuthService implements IAuthService {
if (returnedUrl != undefined) { if (returnedUrl != undefined) {
this.endSessionCallback(); this.endSessionCallback();
} }
} else {
//if user has no token they should not be logged in in the first place
this.endSessionCallback();
} }
} }
@@ -460,13 +467,13 @@ export abstract class AuthService implements IAuthService {
public async getValidToken(buffer: number = AUTH_EXPIRY_BUFFER): Promise<TokenResponse> { public async getValidToken(buffer: number = AUTH_EXPIRY_BUFFER): Promise<TokenResponse> {
if (this._tokenSubject.value) { if (this._tokenSubject.value) {
if (!this._tokenSubject.value.isValid(buffer)) { if (this._tokenSubject.value.isValid(buffer)) {
return this._tokenSubject.value;
} else {
await this.refreshToken(); await this.refreshToken();
if (this._tokenSubject.value) { if (this._tokenSubject.value) {
return this._tokenSubject.value; return this._tokenSubject.value;
} }
} else {
return this._tokenSubject.value;
} }
} }

View File

@@ -28,14 +28,18 @@ export class CapacitorRequestor extends Requestor {
if (!settings.method) settings.method = 'GET'; if (!settings.method) settings.method = 'GET';
switch (settings.method) { switch (settings.method) {
case 'GET': case 'GET': {
return this.get(settings.url, settings.headers); return this.get(settings.url, settings.headers);
case 'POST': }
case 'POST': {
return this.post(settings.url, settings.data, settings.headers); return this.post(settings.url, settings.data, settings.headers);
case 'PUT': }
case 'PUT': {
return this.put(settings.url, settings.data, settings.headers); return this.put(settings.url, settings.data, settings.headers);
case 'DELETE': }
case 'DELETE': {
return this.delete(settings.url, settings.headers); return this.delete(settings.url, settings.headers);
}
} }
} }

View File

@@ -33,26 +33,30 @@ export class NgHttpService implements Requestor {
let observable: Observable<T>; let observable: Observable<T>;
switch (settings.method) { switch (settings.method) {
case 'GET': case 'GET': {
observable = this.http.get<T>(settings.url, { observable = this.http.get<T>(settings.url, {
headers: this.getHeaders(settings.headers), headers: this.getHeaders(settings.headers),
}); });
break; break;
case 'POST': }
case 'POST': {
observable = this.http.post<T>(settings.url, settings.data, { observable = this.http.post<T>(settings.url, settings.data, {
headers: this.getHeaders(settings.headers), headers: this.getHeaders(settings.headers),
}); });
break; break;
case 'PUT': }
case 'PUT': {
observable = this.http.put<T>(settings.url, settings.data, { observable = this.http.put<T>(settings.url, settings.data, {
headers: this.getHeaders(settings.headers), headers: this.getHeaders(settings.headers),
}); });
break; break;
case 'DELETE': }
case 'DELETE': {
observable = this.http.delete<T>(settings.url, { observable = this.http.delete<T>(settings.url, {
headers: this.getHeaders(settings.headers), headers: this.getHeaders(settings.headers),
}); });
break; break;
}
} }
return firstValueFrom(observable); return firstValueFrom(observable);

View File

@@ -60,7 +60,7 @@ export class PAIAAuthorizationRequestHandler {
const returnedUrl: string | undefined = await this.browser.showWindow(url, request.redirectUri); const returnedUrl: string | undefined = await this.browser.showWindow(url, request.redirectUri);
// callback may come from showWindow or via another method // callback may come from showWindow or via another method
if (typeof returnedUrl !== 'undefined') { if (returnedUrl !== undefined) {
await this.storage.setItem(AUTHORIZATION_RESPONSE_KEY, returnedUrl); await this.storage.setItem(AUTHORIZATION_RESPONSE_KEY, returnedUrl);
await this.completeAuthorizationRequestIfPossible(); await this.completeAuthorizationRequestIfPossible();
} }
@@ -88,7 +88,7 @@ export class PAIAAuthorizationRequestHandler {
return <PAIAAuthorizationRequestResponse>{ return <PAIAAuthorizationRequestResponse>{
request: request, // request request: request, // request
response: !error ? this.getAuthorizationResponse(queryParameters) : undefined, response: error ? undefined : this.getAuthorizationResponse(queryParameters),
error: error ? this.getAuthorizationError(queryParameters) : undefined, error: error ? this.getAuthorizationError(queryParameters) : undefined,
}; };
} }
@@ -128,14 +128,14 @@ export class PAIAAuthorizationRequestHandler {
} }
private getQueryParams(authResponse: string | null): StringMap { private getQueryParams(authResponse: string | null): StringMap {
if (authResponse != undefined) { if (authResponse == undefined) {
return {};
} else {
const querySide: string = authResponse.split('#')[0]; const querySide: string = authResponse.split('#')[0];
const parts: string[] = querySide.split('?'); const parts: string[] = querySide.split('?');
if (parts.length !== 2) throw new Error('Invalid auth response string'); if (parts.length !== 2) throw new Error('Invalid auth response string');
const hash = parts[1]; const hash = parts[1];
return this.utils.parseQueryString(hash); return this.utils.parseQueryString(hash);
} else {
return {};
} }
} }

View File

@@ -166,35 +166,42 @@ export class PAIAAuthService {
switch (action.action) { switch (action.action) {
case AuthActions.SignInFailed: case AuthActions.SignInFailed:
case AuthActions.SignOutSuccess: case AuthActions.SignOutSuccess:
case AuthActions.SignOutFailed: case AuthActions.SignOutFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
break; break;
case AuthActions.LoadTokenFromStorageFailed: }
case AuthActions.LoadTokenFromStorageFailed: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
this._userSubject.next(undefined); this._userSubject.next(undefined);
this._authenticatedSubject.next(false); this._authenticatedSubject.next(false);
this._initComplete.next(true); this._initComplete.next(true);
break; break;
case AuthActions.SignInSuccess: }
case AuthActions.SignInSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next(true); this._authenticatedSubject.next(true);
break; break;
case AuthActions.LoadTokenFromStorageSuccess: }
case AuthActions.LoadTokenFromStorageSuccess: {
this._tokenSubject.next(action.tokenResponse); this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0)); this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
this._initComplete.next(true); this._initComplete.next(true);
break; break;
case AuthActions.RevokeTokensSuccess: }
case AuthActions.RevokeTokensSuccess: {
this._tokenSubject.next(undefined); this._tokenSubject.next(undefined);
break; break;
case AuthActions.LoadUserInfoSuccess: }
case AuthActions.LoadUserInfoSuccess: {
this._userSubject.next(action.user); this._userSubject.next(action.user);
break; break;
case AuthActions.LoadUserInfoFailed: }
case AuthActions.LoadUserInfoFailed: {
this._userSubject.next(undefined); this._userSubject.next(undefined);
break; break;
}
} }
this._authSubjectV2.next(action); this._authSubjectV2.next(action);
@@ -219,10 +226,10 @@ export class PAIAAuthService {
if (response != undefined) { if (response != undefined) {
this.requestAccessToken(response.code, response.patron, codeVerifier); this.requestAccessToken(response.code, response.patron, codeVerifier);
} else if (error != undefined) { } else if (error == undefined) {
throw new Error(error.errorDescription);
} else {
throw new Error('Unknown Error With Authentication'); throw new Error('Unknown Error With Authentication');
} else {
throw new Error(error.errorDescription);
} }
} }

View File

@@ -93,7 +93,11 @@ export class ScheduleSyncService implements OnDestroy {
}, },
); );
if (status !== BackgroundFetch.STATUS_AVAILABLE) { if (status === BackgroundFetch.STATUS_AVAILABLE) {
console.info('Starting background fetch.');
await BackgroundFetch.start();
} else {
if (status === BackgroundFetch.STATUS_DENIED) { if (status === BackgroundFetch.STATUS_DENIED) {
console.error( console.error(
'The user explicitly disabled background behavior for this app or for the whole system.', 'The user explicitly disabled background behavior for this app or for the whole system.',
@@ -101,10 +105,6 @@ export class ScheduleSyncService implements OnDestroy {
} else if (status === BackgroundFetch.STATUS_RESTRICTED) { } else if (status === BackgroundFetch.STATUS_RESTRICTED) {
console.error('Background updates are unavailable and the user cannot enable them again.'); console.error('Background updates are unavailable and the user cannot enable them again.');
} }
} else {
console.info('Starting background fetch.');
await BackgroundFetch.start();
} }
} }
} }

View File

@@ -94,7 +94,7 @@ export class ConfigProvider {
* @param attribute requested attribute from app configuration * @param attribute requested attribute from app configuration
*/ */
public getValue(attribute: keyof SCAppConfiguration) { public getValue(attribute: keyof SCAppConfiguration) {
if (typeof this.config.app[attribute] !== 'undefined') { if (this.config.app[attribute] !== undefined) {
return this.config.app[attribute]; return this.config.app[attribute];
} }
throw new ConfigValueNotAvailable(attribute); throw new ConfigValueNotAvailable(attribute);
@@ -106,7 +106,7 @@ export class ConfigProvider {
* @param attribute requested attribute from the configuration * @param attribute requested attribute from the configuration
*/ */
public getAnyValue(attribute: keyof SCIndexResponse) { public getAnyValue(attribute: keyof SCIndexResponse) {
if (typeof this.config[attribute] !== 'undefined') { if (this.config[attribute] !== undefined) {
return this.config[attribute]; return this.config[attribute];
} }
throw new ConfigValueNotAvailable(attribute); throw new ConfigValueNotAvailable(attribute);
@@ -141,13 +141,13 @@ export class ConfigProvider {
fetchError = error; fetchError = error;
} }
// check for occurred errors and throw them // check for occurred errors and throw them
if (typeof loadError !== 'undefined' && typeof fetchError !== 'undefined') { if (loadError !== undefined && fetchError !== undefined) {
throw new ConfigInitError(); throw new ConfigInitError();
} }
if (typeof loadError !== 'undefined') { if (loadError !== undefined) {
this.logger.warn(loadError); this.logger.warn(loadError);
} }
if (typeof fetchError !== 'undefined') { if (fetchError !== undefined) {
this.logger.warn(fetchError); this.logger.warn(fetchError);
} }
} }

View File

@@ -67,7 +67,7 @@ export class DashboardProvider {
from: from, from: from,
}; };
if (typeof filters !== 'undefined') { if (filters !== undefined) {
for (const filter of filters) { for (const filter of filters) {
((query.filter as SCSearchBooleanFilter).arguments as SCBooleanFilterArguments).filters.push(filter); ((query.filter as SCSearchBooleanFilter).arguments as SCBooleanFilterArguments).filters.push(filter);
} }

View File

@@ -189,15 +189,18 @@ export class AddEventActionChipComponent implements OnDestroy {
return; return;
} }
switch (associatedDateSeries.map(it => it.uid).filter(it => !this.uuids.includes(it)).length) { switch (associatedDateSeries.map(it => it.uid).filter(it => !this.uuids.includes(it)).length) {
case 0: case 0: {
this.applyState(AddEventStates.ADDED_ALL); this.applyState(AddEventStates.ADDED_ALL);
break; break;
case associatedDateSeries.length: }
case associatedDateSeries.length: {
this.applyState(AddEventStates.REMOVED_ALL); this.applyState(AddEventStates.REMOVED_ALL);
break; break;
default: }
default: {
this.applyState(AddEventStates.ADDED_SOME); this.applyState(AddEventStates.ADDED_SOME);
break; break;
}
} }
}); });
} }

View File

@@ -34,11 +34,7 @@ export class DataFacetsProvider {
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
addBuckets(bucketsMap: {[key: string]: number}, fields: string[]): {[key: string]: number} { addBuckets(bucketsMap: {[key: string]: number}, fields: string[]): {[key: string]: number} {
for (const field of fields) { for (const field of fields) {
if (typeof bucketsMap[field] !== 'undefined') { bucketsMap[field] = bucketsMap[field] === undefined ? 1 : bucketsMap[field] + 1;
bucketsMap[field] = bucketsMap[field] + 1;
} else {
bucketsMap[field] = 1;
}
} }
return bucketsMap; return bucketsMap;
@@ -82,13 +78,13 @@ export class DataFacetsProvider {
for (const item of items) { for (const item of items) {
for (const aggregation of aggregations) { for (const aggregation of aggregations) {
let fieldValues = item[aggregation.fieldName as keyof SCThing] as string | string[] | undefined; let fieldValues = item[aggregation.fieldName as keyof SCThing] as string | string[] | undefined;
if (typeof fieldValues === 'undefined') { if (fieldValues === undefined) {
continue; continue;
} }
if (typeof fieldValues === 'string') { if (typeof fieldValues === 'string') {
fieldValues = [fieldValues]; fieldValues = [fieldValues];
} }
if (typeof aggregation.onlyOnTypes === 'undefined') { if (aggregation.onlyOnTypes === undefined) {
combinedFacetsMap[aggregation.fieldName] = this.addBuckets( combinedFacetsMap[aggregation.fieldName] = this.addBuckets(
combinedFacetsMap[aggregation.fieldName] || {}, combinedFacetsMap[aggregation.fieldName] || {},
fieldValues, fieldValues,

View File

@@ -133,7 +133,7 @@ export class DataProvider {
for (const item of items) { for (const item of items) {
const value = const value =
typeof bucketMap.get(item.type) === 'undefined' ? 1 : (bucketMap.get(item.type) as number) + 1; bucketMap.get(item.type) === undefined ? 1 : (bucketMap.get(item.type) as number) + 1;
bucketMap.set(item.type, value); bucketMap.set(item.type, value);
} }
@@ -169,7 +169,7 @@ export class DataProvider {
created: new Date().toISOString(), created: new Date().toISOString(),
type: SCThingOriginType.User, type: SCThingOriginType.User,
}, },
type: typeof type === 'undefined' ? item.type : type, type: type === undefined ? item.type : type,
uid: item.uid, uid: item.uid,
}; };
} }

View File

@@ -81,7 +81,7 @@ export class DataDetailComponent implements ViewWillEnter {
* Type guard for SCSavableThing * Type guard for SCSavableThing
*/ */
static isSCSavableThing(thing: SCThings | SCSaveableThing): thing is SCSaveableThing { static isSCSavableThing(thing: SCThings | SCSaveableThing): thing is SCSaveableThing {
return typeof (thing as SCSaveableThing).data !== 'undefined'; return (thing as SCSaveableThing).data !== undefined;
} }
/** /**
@@ -126,12 +126,13 @@ export class DataDetailComponent implements ViewWillEnter {
) )
: this.dataProvider.get(uid, DataScope.Remote))); : this.dataProvider.get(uid, DataScope.Remote)));
this.item = !item this.item = item
? // eslint-disable-next-line unicorn/no-null ? // eslint-disable-next-line unicorn/no-null
null DataDetailComponent.isSCSavableThing(item)
: DataDetailComponent.isSCSavableThing(item)
? item.data ? item.data
: item; : item
// eslint-disable-next-line unicorn/no-null
: null;
} catch { } catch {
// eslint-disable-next-line unicorn/no-null // eslint-disable-next-line unicorn/no-null
this.item = null; this.item = null;
@@ -150,7 +151,7 @@ export class DataDetailComponent implements ViewWillEnter {
.get(uid) .get(uid)
.pipe(take(1)) .pipe(take(1))
.subscribe(item => { .subscribe(item => {
if (typeof item !== 'undefined') { if (item !== undefined) {
this.item = item.data; this.item = item.data;
} }
}); });

View File

@@ -41,7 +41,7 @@ export class FavoriteButtonComponent {
this._item = item; this._item = item;
this.isFavorite$ = this.favoritesService.get(this.item.uid).pipe( this.isFavorite$ = this.favoritesService.get(this.item.uid).pipe(
map(favorite => { map(favorite => {
return typeof favorite !== 'undefined'; return favorite !== undefined;
}), }),
); );
} }

View File

@@ -60,7 +60,7 @@ export class TitleCardComponent implements OnInit, OnChanges {
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
checkTextElipsis() { checkTextElipsis() {
if (typeof this.accordionTextArea === 'undefined') { if (this.accordionTextArea === undefined) {
return; return;
} }
const element = this.accordionTextArea.nativeElement as HTMLElement; const element = this.accordionTextArea.nativeElement as HTMLElement;

View File

@@ -106,7 +106,7 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (Array.isArray(this.items) && typeof changes.items !== 'undefined') { if (Array.isArray(this.items) && changes.items !== undefined) {
this.itemStream.next(this.items); this.itemStream.next(this.items);
this.infiniteScroll.complete(); this.infiniteScroll.complete();
} }
@@ -120,7 +120,7 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.calcSkeletonItems(); this.calcSkeletonItems();
if (typeof this.resetToTop !== 'undefined') { if (this.resetToTop !== undefined) {
this.subscriptions.push( this.subscriptions.push(
this.resetToTop.subscribe(() => { this.resetToTop.subscribe(() => {
// this.viewPort.scrollToIndex(0); // this.viewPort.scrollToIndex(0);

View File

@@ -90,10 +90,10 @@ export class ScThingListItemVirtualScrollStrategy implements VirtualScrollStrate
for (const node of renderedItems) { for (const node of renderedItems) {
const [item, group] = this.getItemByNode(node, renderedItems); const [item, group] = this.getItemByNode(node, renderedItems);
if (!this.heights.has(item)) { if (this.heights.has(item)) {
this.intersectionObserver.observe(node);
} else {
node.style.height = `${this.getHeight(item, group)}px`; node.style.height = `${this.getHeight(item, group)}px`;
} else {
this.intersectionObserver.observe(node);
} }
} }
}); });

View File

@@ -202,7 +202,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
} }
for (const filter of [this.forcedFilter, this.filterQuery]) { for (const filter of [this.forcedFilter, this.filterQuery]) {
if (typeof filter !== 'undefined') { if (filter !== undefined) {
filters.push(filter); filters.push(filter);
} }
} }
@@ -324,7 +324,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
this.filterQuery = query[1]; this.filterQuery = query[1];
this.sortQuery = query[2]; this.sortQuery = query[2];
this.from = 0; this.from = 0;
if (typeof this.filterQuery !== 'undefined' || this.queryText?.length > 0 || this.showDefaultData) { if (this.filterQuery !== undefined || this.queryText?.length > 0 || this.showDefaultData) {
await this.fetchAndUpdateItems(); await this.fetchAndUpdateItems();
this.queryChanged.next(); this.queryChanged.next();
} }

View File

@@ -50,7 +50,7 @@ export class PlaceDetailContentComponent implements OnInit, OnDestroy {
* @param item TODO * @param item TODO
*/ */
hasCategories(item: SCThings): item is SCThings & {categories: string[]} { hasCategories(item: SCThings): item is SCThings & {categories: string[]} {
return typeof (item as {categories: string[]}).categories !== 'undefined'; return (item as {categories: string[]}).categories !== undefined;
} }
/** /**

View File

@@ -92,7 +92,7 @@ export class FavoritesPageComponent extends SearchPageComponent implements OnIni
this.filterQuery = query[1]; this.filterQuery = query[1];
this.sortQuery = query[2]; this.sortQuery = query[2];
this.from = 0; this.from = 0;
if (typeof this.filterQuery !== 'undefined' || this.queryText?.length > 0 || this.showDefaultData) { if (this.filterQuery !== undefined || this.queryText?.length > 0 || this.showDefaultData) {
await this.fetchAndUpdateItems(); await this.fetchAndUpdateItems();
this.queryChanged.next(); this.queryChanged.next();
} }

View File

@@ -187,7 +187,7 @@ export class FavoritesService {
return this.favoritesChanged$.pipe( return this.favoritesChanged$.pipe(
map(favoritesMap => { map(favoritesMap => {
let items = [...favoritesMap.values()].map(favorite => favorite.data); let items = [...favoritesMap.values()].map(favorite => favorite.data);
if (typeof queryText !== 'undefined') { if (queryText !== undefined) {
const textFilteredItems: SCIndexableThings[] = []; const textFilteredItems: SCIndexableThings[] = [];
for (const item of items) { for (const item of items) {
if ( if (
@@ -199,7 +199,7 @@ export class FavoritesService {
items = textFilteredItems; items = textFilteredItems;
} }
if (typeof filterQuery !== 'undefined') { if (filterQuery !== undefined) {
const filterType = FavoritesService.getFilterType( const filterType = FavoritesService.getFilterType(
filterQuery as SCSearchBooleanFilter | SCSearchValueFilter, filterQuery as SCSearchBooleanFilter | SCSearchValueFilter,
); );
@@ -212,7 +212,7 @@ export class FavoritesService {
items = filteredItems; items = filteredItems;
} }
if (typeof sortQuery !== 'undefined') { if (sortQuery !== undefined) {
items = this.sortItems(items, sortQuery[0].arguments.field as 'name' | 'type', sortQuery[0].order); items = this.sortItems(items, sortQuery[0].arguments.field as 'name' | 'type', sortQuery[0].order);
} }

View File

@@ -74,7 +74,7 @@ export class DaiaAvailabilityComponent extends DataDetailComponent implements On
*/ */
async getAvailability(uid: SCUuid) { async getAvailability(uid: SCUuid) {
this.daiaDataProvider.getAvailability(uid).then(holdings => { this.daiaDataProvider.getAvailability(uid).then(holdings => {
if (typeof holdings !== 'undefined') { if (holdings !== undefined) {
this.holdings = holdings; this.holdings = holdings;
this.holdingsByDepartments = groupByStable(holdings, holding => holding.department.id); this.holdingsByDepartments = groupByStable(holdings, holding => holding.department.id);
} }

View File

@@ -66,7 +66,7 @@ export class DaiaDataProvider {
} }
async getAvailability(id: string): Promise<DaiaHolding[] | undefined> { async getAvailability(id: string): Promise<DaiaHolding[] | undefined> {
if (typeof this.daiaServiceUrl === 'undefined') { if (this.daiaServiceUrl === undefined) {
try { try {
const features = this.configProvider.getValue('features') as SCFeatureConfiguration; const features = this.configProvider.getValue('features') as SCFeatureConfiguration;
if (features.extern?.daia?.url) { if (features.extern?.daia?.url) {
@@ -134,7 +134,7 @@ export class DaiaDataProvider {
dueDate: dueDate, dueDate: dueDate,
online: online:
Array.isArray(available) && Array.isArray(available) &&
typeof available.find(item => item.service === 'remote') !== 'undefined', available.find(item => item.service === 'remote') !== undefined,
available: available:
(Array.isArray(available) && (Array.isArray(available) &&
available.find(item => available.find(item =>
@@ -171,7 +171,7 @@ export class DaiaDataProvider {
} }
getHoldingLink(holding: DaiaHolding, open = false) { getHoldingLink(holding: DaiaHolding, open = false) {
if (typeof this.hebisProxyUrl === 'undefined') { if (this.hebisProxyUrl === undefined) {
this.logger.error('HeBIS proxy url undefined'); this.logger.error('HeBIS proxy url undefined');
return; return;
@@ -194,7 +194,7 @@ export class DaiaDataProvider {
unavailable: unavailable.findIndex(item => item.service === 'presentation'), unavailable: unavailable.findIndex(item => item.service === 'presentation'),
}; };
if (loan.unavailable !== -1 && typeof unavailable[loan.unavailable].expected !== 'undefined') { if (loan.unavailable !== -1 && unavailable[loan.unavailable].expected !== undefined) {
return 'checked_out'; return 'checked_out';
} }

View File

@@ -83,7 +83,7 @@ export class HebisDataProvider extends DataProvider {
let page: number | undefined = searchRequest.page; let page: number | undefined = searchRequest.page;
if (typeof page === 'undefined') { if (page === undefined) {
const preFlightResponse = await this.client.invokeRoute<HebisSearchResponse>( const preFlightResponse = await this.client.invokeRoute<HebisSearchResponse>(
this.hebisSearchRoute, this.hebisSearchRoute,
undefined, undefined,

View File

@@ -122,7 +122,7 @@ export class LibraryAccountService {
} }
async getDocumentFromHDS(edition: string) { async getDocumentFromHDS(edition: string) {
if (typeof edition === 'undefined') { if (edition === undefined) {
return; return;
} }
@@ -166,11 +166,13 @@ export class LibraryAccountService {
return new Promise(async resolve => { return new Promise(async resolve => {
const handleDocument = () => { const handleDocument = () => {
switch (documentAction.action) { switch (documentAction.action) {
case 'cancel': case 'cancel': {
return this.cancelReservation(documentAction.doc); return this.cancelReservation(documentAction.doc);
break; break;
case 'renew': }
case 'renew': {
return this.renewLending(documentAction.doc); return this.renewLending(documentAction.doc);
}
} }
}; };
const alert = await this.alertController.create({ const alert = await this.alertController.create({

View File

@@ -75,8 +75,13 @@ export class MapProvider {
icon: divIcon({ icon: divIcon({
className: className, className: className,
html: html:
typeof position.heading !== 'undefined' position.heading === undefined
? `<span ? `<span
name="${SCIcon`person_pin_circle`}"
class="material-symbols-rounded map-location-pin"
style="font-size: ${iconSize}px; color: var(--ion-color-primary);"
>${SCIcon`person_pin_circle`}</span>`
: `<span
class="material-symbols-rounded map-location-pin" class="material-symbols-rounded map-location-pin"
style=" style="
transform-origin: center; transform-origin: center;
@@ -84,12 +89,7 @@ export class MapProvider {
font-size: ${iconSize}px; font-size: ${iconSize}px;
color: var(--ion-color-primary); color: var(--ion-color-primary);
" "
>${SCIcon`navigation`}</span>` >${SCIcon`navigation`}</span>`,
: `<span
name="${SCIcon`person_pin_circle`}"
class="material-symbols-rounded map-location-pin"
style="font-size: ${iconSize}px; color: var(--ion-color-primary);"
>${SCIcon`person_pin_circle`}</span>`,
iconSize: [iconSize, iconSize], iconSize: [iconSize, iconSize],
}), }),
zIndexOffset: 1000, zIndexOffset: 1000,
@@ -202,7 +202,7 @@ export class MapProvider {
let filter = baseFilter; let filter = baseFilter;
if (typeof contextFilter !== 'undefined') { if (contextFilter !== undefined) {
filter = { filter = {
arguments: { arguments: {
operation: 'and', operation: 'and',

View File

@@ -154,7 +154,7 @@ export class MapPageComponent {
* @param latLng Coordinates to animate to * @param latLng Coordinates to animate to
*/ */
private focus(latLng?: LatLng) { private focus(latLng?: LatLng) {
if (typeof latLng !== 'undefined') { if (latLng !== undefined) {
this.map.flyTo(latLng, this.MAX_ZOOM); this.map.flyTo(latLng, this.MAX_ZOOM);
return; return;
@@ -173,7 +173,7 @@ export class MapPageComponent {
this.removeAll(); this.removeAll();
} }
const addSCPlace = (place: SCPlace): Layer | Marker => { const addSCPlace = (place: SCPlace): Layer | Marker => {
if (typeof place.geo.polygon !== 'undefined') { if (place.geo.polygon !== undefined) {
const polygonLayer = geoJSON(place.geo.polygon, { const polygonLayer = geoJSON(place.geo.polygon, {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
@@ -190,7 +190,7 @@ export class MapPageComponent {
items.map(thing => { items.map(thing => {
// IMPORTANT: change this to support inPlace.geo when there is a need to show floors (the building of the floor) // IMPORTANT: change this to support inPlace.geo when there is a need to show floors (the building of the floor)
if (typeof thing.geo !== 'undefined') { if (thing.geo !== undefined) {
this.layers.push(addSCPlace(thing as SCPlace)); this.layers.push(addSCPlace(thing as SCPlace));
} }
}); });
@@ -233,7 +233,7 @@ export class MapPageComponent {
this.items = result.data as SCPlace[]; this.items = result.data as SCPlace[];
this.addToMap(result.data as Array<SCBuilding | SCRoom>, true, animate); this.addToMap(result.data as Array<SCBuilding | SCRoom>, true, animate);
// update filter options if result contains facets // update filter options if result contains facets
if (typeof result.facets !== 'undefined') { if (result.facets !== undefined) {
this.contextMenuService.updateContextFilter(result.facets); this.contextMenuService.updateContextFilter(result.facets);
} }
} catch (error) { } catch (error) {
@@ -313,9 +313,9 @@ export class MapPageComponent {
); );
const uid = this.route.snapshot.paramMap.get('uid'); const uid = this.route.snapshot.paramMap.get('uid');
const response = await (uid !== null const response = await (uid === null
? this.mapProvider.searchPlace(uid) ? this.mapProvider.searchPlaces()
: this.mapProvider.searchPlaces()); : this.mapProvider.searchPlace(uid));
if (response.data.length === 0) { if (response.data.length === 0) {
return; return;
@@ -345,9 +345,9 @@ export class MapPageComponent {
return; return;
} }
this.locationStatus = await (!Capacitor.isNativePlatform() this.locationStatus = await (Capacitor.isNativePlatform()
? Geolocation.checkPermissions() ? Geolocation.requestPermissions()
: Geolocation.requestPermissions()); : Geolocation.checkPermissions());
this.translateService this.translateService
.get(['map.page.geolocation', 'app.errors.UNKNOWN']) .get(['map.page.geolocation', 'app.errors.UNKNOWN'])
@@ -362,9 +362,9 @@ export class MapPageComponent {
message: `${ message: `${
this.locationStatus?.location === 'denied' this.locationStatus?.location === 'denied'
? location.NOT_ALLOWED ? location.NOT_ALLOWED
: this.locationStatus?.location !== 'granted' : this.locationStatus?.location === 'granted'
? location.NOT_ENABLED ? unknownError
: unknownError : location.NOT_ENABLED
}`, }`,
buttons: ['OK'], buttons: ['OK'],
}) })

View File

@@ -54,7 +54,7 @@ export class MapListModalComponent implements OnInit {
*/ */
ngOnInit() { ngOnInit() {
let geofencedFilter: SCSearchBooleanFilter | undefined; let geofencedFilter: SCSearchBooleanFilter | undefined;
if (typeof this.mapBounds !== 'undefined') { if (this.mapBounds !== undefined) {
geofencedFilter = { geofencedFilter = {
arguments: { arguments: {
operation: 'and', operation: 'and',
@@ -77,7 +77,7 @@ export class MapListModalComponent implements OnInit {
}, },
type: 'boolean', type: 'boolean',
}; };
if (typeof this.filterQuery !== 'undefined') { if (this.filterQuery !== undefined) {
geofencedFilter.arguments.filters.push(this.filterQuery); geofencedFilter.arguments.filters.push(this.filterQuery);
} }
} }

Some files were not shown because too many files have changed in this diff Show More