diff --git a/README.md b/README.md index 2312f85..5e6306b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![npm](https://img.shields.io/npm/l/graphql-faker.svg)](https://github.com/graphql-kit/graphql-faker/blob/master/LICENSE) [![docker](https://img.shields.io/docker/build/apisguru/graphql-faker.svg)](https://hub.docker.com/r/apisguru/graphql-faker/) -Mock your future API or extend the existing API with realistic data from [faker.js](https://github.com/Marak/faker.js). **No coding required**. +Mock your future API or extend the existing API with realistic data from [faker.js](https://fakerjs.dev/). **No coding required**. All you need is to write [GraphQL SDL](https://alligator.io/graphql/graphql-sdl/). Don't worry, we will provide you with examples in our SDL editor. In the GIF below we add fields to types inside real GitHub API and you can make queries from GraphiQL, Apollo, Relay, etc. and receive **real data mixed with mock data.** diff --git a/package-lock.json b/package-lock.json index 56aa51c..888cf87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,12 @@ "version": "2.0.0-rc.25", "license": "MIT", "dependencies": { + "@faker-js/faker": "8.0.2", "body-parser": "1.19.0", "chalk": "4.1.0", "cors": "2.8.5", "express": "4.17.1", "express-graphql": "0.12.0", - "faker": "5.5.3", "graphql": "14.7.0", "graphql-voyager": "1.0.0-rc.31", "moment": "2.29.1", @@ -27,7 +27,6 @@ "devDependencies": { "@types/body-parser": "1.19.0", "@types/cors": "2.8.10", - "@types/faker": "5.5.1", "@types/node": "20.2.5", "@types/react": "16.9.35", "@types/react-dom": "16.9.8", @@ -683,6 +682,21 @@ "@f/map-obj": "^1.2.2" } }, + "node_modules/@faker-js/faker": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.0.2.tgz", + "integrity": "sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -896,12 +910,6 @@ "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", "dev": true }, - "node_modules/@types/faker": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.1.tgz", - "integrity": "sha512-JXGjV76oEUZUOSAr3bP5txETYoq0XDOQA8BpOz8Wc3EuvfF7sUVquf/EvM3aphuVKuVaYDSDu523/mAHnqrcvg==", - "dev": true - }, "node_modules/@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -5167,11 +5175,6 @@ "node": ">=0.10.0" } }, - "node_modules/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -13943,6 +13946,11 @@ "@f/map-obj": "^1.2.2" } }, + "@faker-js/faker": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.0.2.tgz", + "integrity": "sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A==" + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -14104,12 +14112,6 @@ "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", "dev": true }, - "@types/faker": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.1.tgz", - "integrity": "sha512-JXGjV76oEUZUOSAr3bP5txETYoq0XDOQA8BpOz8Wc3EuvfF7sUVquf/EvM3aphuVKuVaYDSDu523/mAHnqrcvg==", - "dev": true - }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -17496,11 +17498,6 @@ } } }, - "faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==" - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", diff --git a/package.json b/package.json index 383e881..83ce8c8 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "check:spelling": "cspell --cache --no-progress '**/*'" }, "dependencies": { + "@faker-js/faker": "8.0.2", "body-parser": "1.19.0", "chalk": "4.1.0", "cors": "2.8.5", "express": "4.17.1", "express-graphql": "0.12.0", - "faker": "5.5.3", "graphql": "14.7.0", "graphql-voyager": "1.0.0-rc.31", "moment": "2.29.1", @@ -52,7 +52,6 @@ "devDependencies": { "@types/body-parser": "1.19.0", "@types/cors": "2.8.10", - "@types/faker": "5.5.1", "@types/node": "20.2.5", "@types/react": "16.9.35", "@types/react-dom": "16.9.8", diff --git a/src/fake.ts b/src/fake.ts index 11c533a..5a175e7 100644 --- a/src/fake.ts +++ b/src/fake.ts @@ -1,8 +1,8 @@ -import * as faker from 'faker'; +import { allFakers, faker } from '@faker-js/faker'; import * as moment from 'moment'; export function getRandomInt(min: number, max: number) { - return faker.datatype.number({ min, max }); + return faker.number.int({ min, max }); } export function getRandomItem(array: ReadonlyArray): T { @@ -10,206 +10,205 @@ export function getRandomItem(array: ReadonlyArray): T { } export const stdScalarFakers = { - Int: () => faker.datatype.number({ min: 0, max: 99999, precision: 1 }), - Float: () => faker.datatype.number({ min: 0, max: 99999, precision: 0.01 }), + Int: () => faker.number.int({ min: 0, max: 99999 }), + Float: () => faker.number.float({ min: 0, max: 99999, precision: 0.01 }), String: () => 'string', Boolean: () => faker.datatype.boolean(), - ID: () => toBase64(faker.datatype.number({ max: 9999999999 }).toString()), + ID: () => toBase64(faker.number.int({ max: 9999999999 }).toString()), }; -function toBase64(str) { +function toBase64(str: string) { return Buffer.from(str).toString('base64'); } -const fakeFunctions = { - // Address section - zipCode: () => faker.address.zipCode(), - city: () => faker.address.city(), - // Skipped: faker.address.cityPrefix - // Skipped: faker.address.citySuffix - streetName: () => faker.address.streetName(), - streetAddress: { - args: ['useFullAddress'], - func: (useFullAddress) => faker.address.streetAddress(useFullAddress), - }, - // Skipped: faker.address.streetSuffix - // Skipped: faker.address.streetPrefix - secondaryAddress: () => faker.address.secondaryAddress(), - county: () => faker.address.county(), - country: () => faker.address.country(), - countryCode: () => faker.address.countryCode(), - state: () => faker.address.state(), - stateAbbr: () => faker.address.stateAbbr(), - latitude: () => faker.address.latitude(), - longitude: () => faker.address.longitude(), - - // Commerce section - colorName: () => faker.commerce.color(), - productCategory: () => faker.commerce.department(), - productName: () => faker.commerce.productName(), - money: { - args: ['minMoney', 'maxMoney', 'decimalPlaces'], - func: (min, max, dec) => faker.commerce.price(min, max, dec), - }, - // Skipped: faker.commerce.productAdjective - productMaterial: () => faker.commerce.productMaterial(), - product: () => faker.commerce.product(), - - // Company section - // Skipped: faker.company.companySuffixes - companyName: () => faker.company.companyName(), - // Skipped: faker.company.companySuffix - companyCatchPhrase: () => faker.company.catchPhrase(), - companyBs: () => faker.company.bs(), - // Skipped: faker.company.catchPhraseAdjective - // Skipped: faker.company.catchPhraseDescriptor - // Skipped: faker.company.catchPhraseNoun - // Skipped: faker.company.companyBsAdjective - // Skipped: faker.company.companyBsBuzz - // Skipped: faker.company.companyBsNoun - - // Database section - dbColumn: () => faker.database.column(), - dbType: () => faker.database.type(), - dbCollation: () => faker.database.collation(), - dbEngine: () => faker.database.engine(), - - // Date section - date: { - args: ['dateFormat', 'dateFrom', 'dateTo'], - func: (dateFormat, dateFrom, dateTo) => - moment(faker.date.between(dateFrom, dateTo)) - .format(dateFormat) - .toString(), - }, - pastDate: { - args: ['dateFormat'], - func: (dateFormat) => moment(faker.date.past()).format(dateFormat), - }, - futureDate: { - args: ['dateFormat'], - func: (dateFormat) => moment(faker.date.future()).format(dateFormat), - }, - recentDate: { - args: ['dateFormat'], - func: (dateFormat) => moment(faker.date.recent()).format(dateFormat), - }, - - // Finance section - financeAccountName: () => faker.finance.accountName(), - //TODO: investigate finance.mask - financeTransactionType: () => faker.finance.transactionType(), - currencyCode: () => faker.finance.currencyCode(), - currencyName: () => faker.finance.currencyName(), - currencySymbol: () => faker.finance.currencySymbol(), - bitcoinAddress: () => faker.finance.bitcoinAddress(), - internationalBankAccountNumber: () => faker.finance.iban(), - bankIdentifierCode: () => faker.finance.bic(), - - // Hacker section - hackerAbbreviation: () => faker.hacker.abbreviation(), - hackerPhrase: () => faker.hacker.phrase(), - - // Image section - imageUrl: { - args: ['imageSize', 'imageKeywords', 'randomizeImageUrl'], - func: (size, keywords, randomize) => { - let url = 'https://source.unsplash.com/random/'; - - if (size != null) { - url += `${size.width}x${size.height}/`; - } - - if (keywords != null && keywords.length > 0) { - url += '?' + keywords.join(','); - } - - if (randomize === true) { - url += '#' + faker.datatype.number(); - } - - return url; +function fakeFunctions(fakerInstance: typeof faker) { + allFakers; + return { + // Address section + zipCode: () => fakerInstance.location.zipCode(), + city: () => fakerInstance.location.city(), + // Skipped: faker.address.cityPrefix + // Skipped: faker.address.citySuffix + streetName: () => fakerInstance.location.street(), + streetAddress: { + args: ['useFullAddress'], + func: (useFullAddress) => + fakerInstance.location.streetAddress(useFullAddress), }, - }, - - // Internet section - avatarUrl: () => faker.internet.avatar(), - email: { - args: ['emailProvider'], - func: (provider) => faker.internet.email(undefined, undefined, provider), - }, - url: () => faker.internet.url(), - domainName: () => faker.internet.domainName(), - ipv4Address: () => faker.internet.ip(), - ipv6Address: () => faker.internet.ipv6(), - userAgent: () => faker.internet.userAgent(), - colorHex: { - args: ['baseColor'], - func: ({ red255, green255, blue255 }) => { - return faker.internet.color(red255, green255, blue255); + // Skipped: faker.address.streetSuffix + // Skipped: faker.address.streetPrefix + secondaryAddress: () => fakerInstance.location.secondaryAddress(), + county: () => fakerInstance.location.county(), + country: () => fakerInstance.location.country(), + countryCode: () => fakerInstance.location.countryCode(), + state: () => fakerInstance.location.state(), + stateAbbr: () => fakerInstance.location.state({ abbreviated: true }), + latitude: () => fakerInstance.location.latitude(), + longitude: () => fakerInstance.location.longitude(), + + // Commerce section + colorName: () => fakerInstance.color.human(), + productCategory: () => fakerInstance.commerce.department(), + productName: () => fakerInstance.commerce.productName(), + money: { + args: ['minMoney', 'maxMoney', 'decimalPlaces'], + func: (min, max, dec) => fakerInstance.commerce.price({ min, max, dec }), + }, + // Skipped: faker.commerce.productAdjective + productMaterial: () => fakerInstance.commerce.productMaterial(), + product: () => fakerInstance.commerce.product(), + + // Company section + // Skipped: faker.company.companySuffixes + companyName: () => fakerInstance.company.name(), + // Skipped: faker.company.companySuffix + companyCatchPhrase: () => fakerInstance.company.catchPhrase(), + companyBs: () => fakerInstance.company.buzzPhrase(), + // Skipped: faker.company.catchPhraseAdjective + // Skipped: faker.company.catchPhraseDescriptor + // Skipped: faker.company.catchPhraseNoun + // Skipped: faker.company.companyBsAdjective + // Skipped: faker.company.companyBsBuzz + // Skipped: faker.company.companyBsNoun + + // Database section + dbColumn: () => fakerInstance.database.column(), + dbType: () => fakerInstance.database.type(), + dbCollation: () => fakerInstance.database.collation(), + dbEngine: () => fakerInstance.database.engine(), + + // Date section + date: { + args: ['dateFormat', 'dateFrom', 'dateTo'], + func: (dateFormat, dateFrom, dateTo) => + moment(fakerInstance.date.between({ from: dateFrom, to: dateTo })) + .format(dateFormat) + .toString(), + }, + pastDate: { + args: ['dateFormat'], + func: (dateFormat) => + moment(fakerInstance.date.past()).format(dateFormat), + }, + futureDate: { + args: ['dateFormat'], + func: (dateFormat) => + moment(fakerInstance.date.future()).format(dateFormat), + }, + recentDate: { + args: ['dateFormat'], + func: (dateFormat) => + moment(fakerInstance.date.recent()).format(dateFormat), + }, + + // Finance section + financeAccountName: () => fakerInstance.finance.accountName(), + //TODO: investigate finance.mask + financeTransactionType: () => fakerInstance.finance.transactionType(), + currencyCode: () => fakerInstance.finance.currencyCode(), + currencyName: () => fakerInstance.finance.currencyName(), + currencySymbol: () => fakerInstance.finance.currencySymbol(), + bitcoinAddress: () => fakerInstance.finance.bitcoinAddress(), + internationalBankAccountNumber: () => fakerInstance.finance.iban(), + bankIdentifierCode: () => fakerInstance.finance.bic(), + + // Hacker section + hackerAbbreviation: () => fakerInstance.hacker.abbreviation(), + hackerPhrase: () => fakerInstance.hacker.phrase(), + + // Image section + imageUrl: { + args: ['imageSize', 'imageKeywords', 'randomizeImageUrl'], + func: (size, keywords, randomize) => { + let url = 'https://source.unsplash.com/random/'; + + if (size != null) { + url += `${size.width}x${size.height}/`; + } + + if (keywords != null && keywords.length > 0) { + url += '?' + keywords.join(','); + } + + if (randomize === true) { + url += '#' + fakerInstance.number.int(); + } + + return url; + }, }, - }, - macAddress: () => faker.internet.mac(), - password: { - args: ['passwordLength'], - func: (len) => faker.internet.password(len), - }, - - // Lorem section - lorem: { - args: ['loremSize'], - func: (size) => faker.lorem[size || 'paragraphs'](), - }, - - // Name section - firstName: () => faker.name.firstName(), - lastName: () => faker.name.lastName(), - fullName: () => faker.name.findName(), - jobTitle: () => faker.name.jobTitle(), - - // Phone section - phoneNumber: () => faker.phone.phoneNumber(), - // Skipped: faker.phone.phoneNumberFormat - // Skipped: faker.phone.phoneFormats - - // Random section - number: { - args: ['minNumber', 'maxNumber', 'precisionNumber'], - func: (min, max, precision) => - faker.datatype.number({ min, max, precision }), - }, - uuid: () => faker.random.uuid(), - word: () => faker.random.word(), - words: () => faker.random.words(), - locale: () => faker.random.locale(), - - // System section - // Skipped: faker.system.fileName - // TODO: Add ext - filename: () => faker.system.commonFileName(), - mimeType: () => faker.system.mimeType(), - // Skipped: faker.system.fileType - // Skipped: faker.system.commonFileType - // Skipped: faker.system.commonFileExt - fileExtension: () => faker.system.fileExt(), - semver: () => faker.system.semver(), -}; -Object.keys(fakeFunctions).forEach((key) => { - const value = fakeFunctions[key]; - if (typeof fakeFunctions[key] === 'function') - fakeFunctions[key] = { args: [], func: value }; -}); + // Internet section + avatarUrl: () => fakerInstance.internet.avatar(), + email: { + args: ['emailProvider'], + func: (provider) => fakerInstance.internet.email({ provider }), + }, + url: () => fakerInstance.internet.url(), + domainName: () => fakerInstance.internet.domainName(), + ipv4Address: () => fakerInstance.internet.ip(), + ipv6Address: () => fakerInstance.internet.ipv6(), + userAgent: () => fakerInstance.internet.userAgent(), + colorHex: { + args: ['baseColor'], + func: ({ redBase, greenBase, blueBase }) => { + return fakerInstance.internet.color({ redBase, greenBase, blueBase }); + }, + }, + macAddress: () => fakerInstance.internet.mac(), + password: { + args: ['passwordLength'], + func: (len) => fakerInstance.internet.password(len), + }, + + // Lorem section + lorem: { + args: ['loremSize'], + func: (size) => fakerInstance.lorem[size || 'paragraphs'](), + }, + + // Name section + firstName: () => fakerInstance.person.firstName(), + lastName: () => fakerInstance.person.lastName(), + fullName: () => fakerInstance.person.fullName(), + jobTitle: () => fakerInstance.person.jobTitle(), + + // Phone section + phoneNumber: () => fakerInstance.phone.number(), + // Skipped: faker.phone.phoneNumberFormat + // Skipped: faker.phone.phoneFormats + + // Random section + number: { + args: ['minNumber', 'maxNumber', 'precisionNumber'], + func: (min, max, precision) => + fakerInstance.number.float({ min, max, precision }), + }, + uuid: () => fakerInstance.string.uuid(), + word: () => fakerInstance.lorem.word(), + words: () => fakerInstance.lorem.words(), + locale: () => fakerInstance.helpers.objectKey(allFakers), + + // System section + // Skipped: faker.system.fileName + // TODO: Add ext + filename: () => fakerInstance.system.commonFileName(), + mimeType: () => fakerInstance.system.mimeType(), + // Skipped: faker.system.fileType + // Skipped: faker.system.commonFileType + // Skipped: faker.system.commonFileExt + fileExtension: () => fakerInstance.system.fileExt(), + semver: () => fakerInstance.system.semver(), + }; +} export function fakeValue(type, options?, locale?) { - const fakeGenerator = fakeFunctions[type]; - const argNames = fakeGenerator.args; - //TODO: add check - const callArgs = argNames.map((name) => options[name]); - - const localeBackup = faker.locale; - faker.setLocale(locale || localeBackup); - const result = fakeGenerator.func(...callArgs); - faker.setLocale(localeBackup); - return result; + const fakerInstance = locale != null ? allFakers[locale] : faker; + const fakeGenerator = fakeFunctions(fakerInstance)[type]; + + if (typeof fakeGenerator === 'function') { + return fakeGenerator(); + } + const callArgs = fakeGenerator.args.map((name) => options[name]); + return fakeGenerator.func(...callArgs); } diff --git a/src/fake_definition.ts b/src/fake_definition.ts index 9035ff0..2147989 100644 --- a/src/fake_definition.ts +++ b/src/fake_definition.ts @@ -1,3 +1,4 @@ +import { allFakers } from '@faker-js/faker'; import { buildASTSchema, DocumentNode, @@ -18,43 +19,7 @@ import { validateSDL } from 'graphql/validation/validate'; const fakeDefinitionAST = parse(/* GraphQL */ ` enum fake__Locale { - az - cz - de - de_AT - de_CH - en - en_AU - en_BORK - en_CA - en_GB - en_IE - en_IND - en_US - en_au_ocker - es - es_MX - fa - fr - fr_CA - ge - id_ID - it - ja - ko - nb_NO - nep - nl - pl - pt_BR - ru - sk - sv - tr - uk - vi - zh_CN - zh_TW + ${Object.keys(allFakers).join(' ')} } enum fake__Types {