Skip to content

Commit

Permalink
fix: rename wallet files for better alignment (#98)
Browse files Browse the repository at this point in the history
* fix: rename wallet files for better alignment

Signed-off-by: Mirko Mollik <[email protected]>

* add link checker script

Signed-off-by: Mirko Mollik <[email protected]>

* debug validation

Signed-off-by: Mirko Mollik <[email protected]>

* tmp

Signed-off-by: Mirko Mollik <[email protected]>

* tmp

Signed-off-by: Mirko Mollik <[email protected]>

* check cases when folder does not exist

Signed-off-by: Mirko Mollik <[email protected]>

---------

Signed-off-by: Mirko Mollik <[email protected]>
  • Loading branch information
cre8 authored Oct 7, 2024
1 parent 029dd3b commit 2a301f6
Show file tree
Hide file tree
Showing 72 changed files with 905 additions and 714 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/link-checker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Link checker

on:
schedule:
- cron: '0 0 * * 0' # Runs at midnight every Sunday
workflow_dispatch:

permissions:
contents: write

jobs:
link-checker:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 18

- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: viewer/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: cd viewer && npm install

- name: Validate links
run: cd viewer && node scripts/link-checker.mjs

# Deploy to local repo
- name: Deploy
uses: s0/git-publish-subdir-action@develop
env:
REPO: self
BRANCH: errors
FOLDER: errors
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 2 additions & 1 deletion .github/workflows/newsletter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
inputs:
email:
description: 'Email address to send the newsletter to'
required: true

jobs:
generate-newsletter:
Expand Down Expand Up @@ -40,7 +41,7 @@ jobs:
EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }}
EMAIL_RECIPIENT: ${{ github.event.inputs.email }}
NODE_ENV: ${{ vars.NODE_ENV }}
EMAIL_STORE: true
EMAIL_STORE: true

- name: Upload newsletter artifact
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
errors
117 changes: 117 additions & 0 deletions viewer/scripts/link-checker.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// this script checks if all the links in the json files are still reachable
import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'fs';
import axios from 'axios';
import { join, dirname } from 'path';

let counter = 0;
let validFiles = 0;
let invalidFiles = 0;
const errorLog = {};

async function isLinkReachable(url, filePath, jsonPath) {
try {
const response = await axios.get(url, {
timeout: 10000, // 10 seconds timeout
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; LinkChecker/1.0)' },
maxRedirects: 5
});
return response.status >= 200 && response.status < 400;
} catch (error) {
if (error.code === 'ECONNABORTED') {
console.log(`Request timed out for URL: ${url} in file: ${filePath} at path: ${jsonPath}`);
} else {
console.log(`Error reaching URL: ${url} in file: ${filePath} at path: ${jsonPath} - ${error.message}`);
}
counter++;
if (!errorLog[filePath]) {
errorLog[filePath] = {};
}
errorLog[filePath][jsonPath] = url;
return false;
}
}

async function checkLinksInObject(obj, filePath, currentPath = '') {
const promises = [];
let hasUnreachableLinks = false;

function collectPromises(obj, path) {
for (const key in obj) {
const newPath = path ? `${path}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
collectPromises(obj[key], newPath);
} else if (typeof obj[key] === 'string' && obj[key].includes('http')) {
promises.push(
isLinkReachable(obj[key], filePath, newPath).then(isReachable => {
if (!isReachable) {
console.log(`Unreachable link found in ${filePath} at path: ${newPath}: ${obj[key]}`);
hasUnreachableLinks = true;
}
})
);
}
}
}

collectPromises(obj, currentPath);
await Promise.all(promises);

return !hasUnreachableLinks;
}

async function validateFolder(folder) {
if(!existsSync(folder)) {
return;
}
const files = readdirSync(folder);
const promises = files
.filter(file => file.endsWith('.json'))
.map(async file => {
const content = JSON.parse(readFileSync(`${folder}/${file}`, 'utf8'));
const isValid = await checkLinksInObject(content, `${folder}/${file}`);
if (isValid) {
validFiles++;
} else {
invalidFiles++;
}
});

await Promise.all(promises);
}

const folders = ['case-studies', 'wallets', 'dependencies'];

(async () => {
const errorsDir = '../errors';
if (!existsSync(errorsDir)) {
mkdirSync(errorsDir);
} else {
// delete all files and subdirectories in the errors folder
const files = readdirSync(errorsDir);
for (const file of files) {
rmSync(join(errorsDir, file), { recursive: true, force: true });
}
}

for (const folder of folders) {
counter = 0;
validFiles = 0;
invalidFiles = 0;
await validateFolder('../' + folder);
console.log(`Total unreachable links in ${folder}: ${counter}`);
console.log(`Valid JSON files in ${folder}: ${validFiles}`);
console.log(`Invalid JSON files in ${folder}: ${invalidFiles}`);
}

console.log('\nError Log:');
for (const [filePath, errors] of Object.entries(errorLog)) {
const relativePath = filePath.replace('../', '');
const errorFilePath = join(errorsDir, relativePath);
const errorDir = dirname(errorFilePath);

if (!existsSync(errorDir)) {
mkdirSync(errorDir, { recursive: true });
}
writeFileSync(errorFilePath, JSON.stringify(errors, null, 2));
}
})();
43 changes: 35 additions & 8 deletions viewer/scripts/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,34 @@ addFormats(ajv);
const dependencyIds = [];
const walletIds = [];

// Helper function to normalize filenames
function normalizeFilename(filename) {
return filename.toLowerCase().replace(/\s+/g, '-');
}

// Check files in a folder to ensure they meet the desired format
function checkFilesInFolder(folder) {
if(!existsSync(folder)) {
console.info(`No files found in ${folder}`);
return;
}
const files = readdirSync(folder);
files.forEach(file => {
const newFileName = normalizeFilename(file);
if (file !== newFileName) {
throw new Error(`Invalid filename: ${file} should be ${newFileName}`);
}
});
}

// validate dependencies
function validateDependencies() {
checkFilesInFolder('../dependencies');
const validate = ajv.compile(JSON.parse(readFileSync('src/assets/dependency.schema.json')));
const files = readdirSync('../dependencies');
const files = readdirSync('../dependencies').map(normalizeFilename);
let success = true;
files.map(file => {
const dependency = JSON.parse(readFileSync(`../dependencies/${file}`))
const dependency = JSON.parse(readFileSync(`../dependencies/${file}`));
if(!validate(dependency)) {
console.error(`Error validating ${file}:`);
console.error(JSON.stringify(validate.errors, null, 2));
Expand All @@ -32,13 +53,14 @@ function validateDependencies() {
}

async function validateWallets() {
checkFilesInFolder('../wallets');
const profileSIGSchema = await axios.get('https://openwallet-foundation.github.io/credential-format-comparison-sig/assets/schemas/fields.json').then(res => res.data);
ajv.addSchema(profileSIGSchema, "https://openwallet-foundation.github.io/credential-format-comparison-sig/assets/schemas/fields.json");
const validate = ajv.compile(JSON.parse(readFileSync('src/assets/schema.json')));
const files = readdirSync('../wallets');
const files = readdirSync('../wallets').map(normalizeFilename);
let success = true;
files.map(file => {
const wallet = JSON.parse(readFileSync(`../wallets/${file}`))
const wallet = JSON.parse(readFileSync(`../wallets/${file}`));
if(!validate(wallet)) {
console.error(`Error validating ${file}:`);
console.error(JSON.stringify(validate.errors, null, 2));
Expand All @@ -55,7 +77,6 @@ async function validateWallets() {
}
}
}

});
if(success) {
console.info('All wallets are valid');
Expand All @@ -66,16 +87,20 @@ async function validateWallets() {
}

function validateCaseStudies() {
if(!existsSync("../case-studies")) {
return;
}
checkFilesInFolder('../case-studies');
const validate = ajv.compile(JSON.parse(readFileSync('src/assets/case-study.schema.json')));
// needed in case no folder is there
if(!existsSync('../case-studies')) {
console.info('No case studies found');
return;
}
const files = readdirSync('../case-studies');
const files = readdirSync('../case-studies').map(normalizeFilename);
let success = true;
files.map(file => {
const caseStudy = JSON.parse(readFileSync(`../case-studies/${file}`))
const caseStudy = JSON.parse(readFileSync(`../case-studies/${file}`));
if(!validate(caseStudy)) {
console.error(`Error validating ${file}:`);
console.error(JSON.stringify(validate.errors, null, 2));
Expand All @@ -85,7 +110,7 @@ function validateCaseStudies() {
caseStudy.references.forEach(element => {
if(!walletIds.includes(element)) {
console.error(`Referenced wallet ${element} not found in wallets`);
success = false
success = false;
}
});
});
Expand All @@ -97,6 +122,8 @@ function validateCaseStudies() {
}
}

checkFilesInFolder('../dependencies');
validateDependencies();
await validateWallets();
checkFilesInFolder('../case-studies');
validateCaseStudies();
Loading

0 comments on commit 2a301f6

Please sign in to comment.