Typescript: cannot load json file contents into array

Issue

I need load my apikeys from json file (api.json) with file structure
api.json:

{
"service1": ["apikey1", "apikey2"],
"service2": ["apikey1", "apikey2"],
}

I have a class that describes a key & value:

class ApiKey {
   constructor(apiName: String, apiKeys: Array<String>)
}

And I have a class, that loads all keys from file to Array:

//read file async: https://stackoverflow.com/questions/46867517/how-to-read-file-with-async-await-properly
    import { readFile } from 'fs'
    import { promisify } from 'util'
    import { join } from 'path'

    export class ApikeysService {

    constructor(private apiKeys: Array<ApiKey> = new Array<ApiKey>()) {
        this.loadKeys()
    }

    public loadKeys () {
        const filePath      = join(__dirname, "../../../", "api.json");
        const readfile      = promisify(readFile);
        const result        = readfile(filePath, "utf8")
                                .then(content => JSON.parse(content))
                                .then(result => {
                                    Object.keys(result).forEach(key => {
                                        this.apikeys.push(new ApiKey(key, result[key]))
                                    })
                                })
        
    }

    public getKeyFor(name: String) {
        return this.keyCounters.find(x => x.keyname == name).apiKeys
    }

}

And my tests:

describe('should load api keys', () => { 
    it("should load apikeys from file", async () => {
         const service = new ApikeysService ()
         it("should load api keys for service1", () => {
    

         expect(service.getKeyFor("service1")[0]).toEqual("apikey1")
         expect(service.getKeyFor("service1")[1]).toEqual("apikey2")

    })
})

Test Result:

expect(received).toEqual(expected) // deep equality

>     Expected: "apikey1"
>     Received: undefined

I tried i lot of different ways to load contents from file to array in class (async also) but it wont work

Solution

You are firing an async function in your constructor, and checking the result directly afterwards – you give no chance for the promise to resolve. It is not synchronous.

Either return result from loadKeys and make sure to await it, or change it to become synchronous, using readFileSync.

Example 1

export class ApikeysService {
    constructor(private apiKeys: Array<ApiKey> = new Array<ApiKey>()) {
        this.loadKeys()
    }

    public loadKeys () {
        const filePath      = join(__dirname, "../../../", "api.json");
        const readfile      = promisify(readFile);
        const content       = JSON.parse(readFileSync(filePath, "utf8"));

        Object.keys(result).forEach(key => {
            this.apikeys.push(new ApiKey(key, result[key]))
        })        
    }

    public getKeyFor(name: String) {
        return this.keyCounters.find(x => x.keyname == name).apiKeys
    }
}

Example 2

export class ApikeysService {
    constructor(private apiKeys: Array<ApiKey> = new Array<ApiKey>()) {
        // this.loadKeys()
    }

    public loadKeys () {
        const filePath      = join(__dirname, "../../../", "api.json");
        const readfile      = promisify(readFile);
        const result        = readfile(filePath, "utf8")
                                .then(content => JSON.parse(content))
                                .then(result => {
                                    Object.keys(result).forEach(key => {
                                        this.apikeys.push(new ApiKey(key, result[key]))
                                    })
                                })
        return result;   
    }

    public getKeyFor(name: String) {
        return this.keyCounters.find(x => x.keyname == name).apiKeys
    }
}

describe('should load api keys', () => { 
    it("should load apikeys from file", async () => {
        const service = new ApikeysService ()
        await service.loadKeys();

        it("should load api keys for service1", () => {
            expect(service.getKeyFor("service1")[0]).toEqual("apikey1")
            expect(service.getKeyFor("service1")[1]).toEqual("apikey2")
        })
    })
})

Answered By – casraf

Answer Checked By – David Marino (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.