Skip to content
This repository was archived by the owner on Jan 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env sh
# Pre-commit hook to run Snyk and Talisman scans, completing both before deciding to commit

# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}

# Check if Snyk is installed
if ! command_exists snyk; then
echo "Error: Snyk is not installed. Please install it and try again."
exit 1
fi

# Check if Talisman is installed
if ! command_exists talisman; then
echo "Error: Talisman is not installed. Please install it and try again."
exit 1
fi

# Allow bypassing the hook with an environment variable
if [ "$SKIP_HOOK" = "1" ]; then
echo "Skipping Snyk and Talisman scans (SKIP_HOOK=1)."
exit 0
fi

# Initialize variables to track scan results
snyk_failed=false
talisman_failed=false

# Run Snyk vulnerability scan
echo "Running Snyk vulnerability scan..."
snyk test --all-projects > snyk_output.log 2>&1
snyk_exit_code=$?

if [ $snyk_exit_code -eq 0 ]; then
echo "Snyk scan passed: No vulnerabilities found."
elif [ $snyk_exit_code -eq 1 ]; then
echo "Snyk found vulnerabilities. See snyk_output.log for details."
snyk_failed=true
else
echo "Snyk scan failed with error (exit code $snyk_exit_code). See snyk_output.log for details."
snyk_failed=true
fi

# Run Talisman secret scan (continues even if Snyk failed)
echo "Running Talisman secret scan..."
talisman --githook pre-commit > talisman_output.log 2>&1
talisman_exit_code=$?

if [ $talisman_exit_code -eq 0 ]; then
echo "Talisman scan passed: No secrets found."
else
echo "Talisman scan failed (exit code $talisman_exit_code). See talisman_output.log for details."
talisman_failed=true
fi

# Evaluate results after both scans
if [ "$snyk_failed" = true ] || [ "$talisman_failed" = true ]; then
echo "Commit aborted due to issues found in one or both scans."
[ "$snyk_failed" = true ] && echo "- Snyk issues: Check snyk_output.log"
[ "$talisman_failed" = true ] && echo "- Talisman issues: Check talisman_output.log"
exit 1
fi

# If both scans pass, allow the commit
echo "All scans passed. Proceeding with commit.cd ."
rm -f snyk_output.log talisman_output.log
exit 0
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Changelog

## [v2.0.0](https://github.com/contentstack/contentstack-graphql-schema-download/tree/v2.0.0) (2025-05-12)
- Feature
- Support Node version 18 or later versions
- Fixed security issues and version bumps.

## [v1.0.0](https://github.com/contentstack/contentstack-graphql-schema-download/tree/v1.0.0) (2020-07-29)
- Initial release for Contentstack Graphql Schema Download
2 changes: 1 addition & 1 deletion LICENCE → LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Contentstack
Copyright (c) 2020-2025 Contentstack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Downloading schema is the process of creating a single GraphQL schema from the C
Contentstack GraphQL provides a schema response with a limit of 100 Content Types. Therefore, to create a single ```schema.json``` file, we need to paginate through the GraphQL. This library will help you to generate a single schema file by paginating and merging the schema.

## Prerequisite
You need Node.js version 10 or later installed on your machine
You need Node.js version 18 or later installed on your machine

## Setup and Installation
- Clone this project by using the following command:
Expand All @@ -31,6 +31,9 @@ const config = {
fileName: '<SCHEMA_FILE_NAME>'
}
```
#### 🔗 Choosing the Right Host:
Refer to the [GraphQL Content Delivery API documentation](https://www.contentstack.com/docs/developers/apis/graphql-content-delivery-api)
to determine the correct `host` value based on your region and environment.

## Usage
- Once you have updated the configuration as shown above, you can use following command to download the schema:
Expand All @@ -44,7 +47,7 @@ const config = {
- iOS requires a GraphQL schema file as input to the code generation process. A schema file is a JSON file that contains the results of an introspection query. Conventionally, this file is called schema.json.
- Note that you are required to add this in the folder where most of your code is, and NOT in the same folder where the .xcodeproj and/or .xcworkspace are located.

Refer the [Adding a schema file to your target directory](https://www.apollographql.com/docs/ios/installation/#adding-a-schema-file-to-your-target-directory) doc for more information.
Refer the [Adding a schema file to your target directory](https://www.apollographql.com/docs/ios/get-started) doc for more information.

### Android
- Android requires your GraphQL server's schema as a schema.json file. You can obtain the content of this file by running an introspection query on your server.
Expand All @@ -53,5 +56,5 @@ const config = {
src/main/graphql/com/example/schema.json
```

Refer the [Add your query](https://www.apollographql.com/docs/android/essentials/get-started-kotlin/#add-your-query) doc for more information.
Refer the [Add your query](https://www.apollographql.com/docs/kotlin/v2/essentials/get-started-kotlin) doc for more information.

27 changes: 27 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Security

Contentstack takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations.

If you believe you have found a security vulnerability in any Contentstack-owned repository, please report it to us as described below.

## Reporting Security Issues

**Please do not report security vulnerabilities through public GitHub issues.**

Send email to [[email protected]](mailto:[email protected]).

You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message.

Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:

* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue

This information will help us triage your report more quickly.

[https://www.contentstack.com/trust/](https://www.contentstack.com/trust/)
1 change: 1 addition & 0 deletions config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const config = {
host: '<HOST>',
// deepcode ignore HardcodedNonCryptoSecret: it's not a secret
apiKey: '<STACK_API_KEY>',
deliveryToken: '<ENVIRONMENT_SPECIFIC_DELIVERY_TOKEN>',
environment: '<ENVIRONMENT>',
Expand Down
100 changes: 39 additions & 61 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,58 @@
const path = require('path')
const { config } = require('../config')
const { introspectionFromSchema, print } = require('graphql')
const {
mergeSchemas,
makeRemoteExecutableSchema,
loadSchema,
UrlLoader
} = require('graphql-tools')
const { writeFileSync } = require('mz/fs')
const package = require('../package.json')
var fetch = require('node-fetch')
const graphqlApis = `https://${config.host}/stacks/${config.apiKey}?environment=${config.environment}`

var limit = 100
console.log('Loading Contentstack Schema download')
const path = require('path');
const { config } = require('../config');
const { introspectionFromSchema } = require('graphql');
const { loadSchema } = require('@graphql-tools/load');
const { UrlLoader } = require('@graphql-tools/url-loader');
const { mergeTypeDefs } = require('@graphql-tools/merge');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { writeFileSync } = require('mz/fs');
const pkg = require('../package.json');
const fetch = require('node-fetch'); // Ensure this is installed

const graphqlApis = `https://${config.host}/stacks/${config.apiKey}?environment=${config.environment}`;

const limit = 100;
console.log('Loading Contentstack Schema download...');

const fetchSchemas = async () => {
var schemas = []
var skip = 0
const schemas = [];
let skip = 0;

while (skip < config.contentTypes) {
console.log(`Fetching ${config.contentTypes > limit ? skip + limit : config.contentTypes} of ${config.contentTypes} Content Types.`)
const currentFetchCount = Math.min(skip + limit, config.contentTypes);
console.log(`Fetching ${currentFetchCount} of ${config.contentTypes} Content Types.`);

const url = `${graphqlApis}&limit=${limit}&skip=${skip}`
const url = `${graphqlApis}&limit=${limit}&skip=${skip}`;

// Fetch Remote Schema with loadSchema methid
const remoteSchema = await loadSchema(url, {
headers: {
access_token: config.deliveryToken,
'X-User-Agent': `${package.name}/${package.version}`
'X-User-Agent': `${pkg.name}/${pkg.version}`
},
loaders: [
new UrlLoader()
]
})
fetch,
loaders: [new UrlLoader()]
});

// Create executer for schema
const executor = async ({ document, variables }) => {
const query = print(document)
const fetchC = await fetch(url,
{
method: 'POST',
headers: {
access_token: config.deliveryToken
},
body: JSON.stringify({ query, variables })
})
return fetchC.json()
}

// Make remote Schema Executable with executor
const remoteExecutableSchema = makeRemoteExecutableSchema({
schema: remoteSchema,
executor
})
schemas.push(remoteExecutableSchema)
skip += limit
schemas.push(remoteSchema);
skip += limit;
}

console.log(`${config.contentTypes} Content Types schema loaded.`)
console.log(`${config.contentTypes} Content Types schema loaded.`);

// Merge schemas from loaded schema
const schema = mergeSchemas({
schemas
})
const mergedTypeDefs = mergeTypeDefs(schemas);
const schema = makeExecutableSchema({ typeDefs: mergedTypeDefs });

console.log(`Saving schema to ${config.fileName}`)
const pathForSchema = path.join(__dirname, `${config.fileName}`);
console.log(`Saving schema to ${pathForSchema}`);

// Write to file
const pathForSchema = path.join(__dirname, `${config.fileName}`)
writeFileSync(
pathForSchema,
JSON.stringify(introspectionFromSchema(schema), null, 2)
)
}
);

console.log('Schema saved successfully.');
};

fetchSchemas()
.catch((err) => {
console.error(err.extensions.errors)
})
fetchSchemas().catch((err) => {
console.error('Failed to fetch schemas:', err);
});
Loading
Loading