Microsoft Graph SDK for Go - Core Library

Overview

Microsoft Graph Core SDK for Go

PkgGoDev Coverage Sonarcloud Status

Get started with the Microsoft Graph Core SDK for Go by integrating the Microsoft Graph API into your Go application! You can also have a look at the Go documentation

Note: Although you can use this library directly, we recommend you use the v1 or beta library which rely on this library and additionally provide a fluent style Go API and models.

Note: the Microsoft Graph Go SDK is currently in Community Preview. During this period we're expecting breaking changes to happen to the SDK based on community's feedback. Checkout the known limitations.

Samples and usage guide

1. Installation

go get -u github.com/microsoftgraph/msgraph-sdk-go-core

2. Getting started

2.1 Register your application

Register your application by following the steps at Register your app with the Microsoft Identity Platform.

2.2 Create an AuthenticationProvider object

An instance of the GraphRequestAdapterBase class handles building client. To create a new instance of this class, you need to provide an instance of AuthenticationProvider, which can authenticate requests to Microsoft Graph.

For an example of how to get an authentication provider, see choose a Microsoft Graph authentication provider.

Note: we are working to add the getting started information for Go to our public documentation, in the meantime the following sample should help you getting started.

import (
    azidentity "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    a          "github.com/microsoft/kiota/authentication/go/azure"
    "context"
)

cred, err := azidentity.NewDeviceCodeCredential(&azidentity.DeviceCodeCredentialOptions{
    TenantID: "<the tenant id from your app registration>",
    ClientID: "<the client id from your app registration>",
    UserPrompt: func(ctx context.Context, message azidentity.DeviceCodeMessage) error {
        fmt.Println(message.Message)
        return nil
    },
})

if err != nil {
    fmt.Printf("Error creating credentials: %v\n", err)
}

auth, err := a.NewAzureIdentityAuthenticationProviderWithScopes(cred, []string{"Mail.Read", "Mail.Send"})
if err != nil {
    fmt.Printf("Error authentication provider: %v\n", err)
    return
}

2.3 Get a Request Adapter object

You must get a GraphRequestAdapterBase object to make requests against the service.

import core "github.com/microsoftgraph/msgraph-sdk-go-core"

adapter, err := core.NewGraphRequestAdapterBase(auth)
if err != nil {
    fmt.Printf("Error creating adapter: %v\n", err)
    return
}

3. Make requests against the service

After you have a GraphRequestAdapterBase that is authenticated, you can begin making calls against the service. The requests against the service look like our REST API.

3.1 Get the user's details

To retrieve the user's details

import abs "github.com/microsoft/kiota/abstractions/go"

requestInf := abs.NewRequestInformation()
targetUrl, err := url.Parse("https://graph.microsoft.com/v1.0/me")
if err != nil {
    fmt.Printf("Error parsing URL: %v\n", err)
}
requestInf.SetUri(*targetUrl)

// User is your own type that implements Parsable or comes from the service library
user, err := adapter.SendAsync(*requestInf, func() { return &User }, nil)

if err != nil {
    fmt.Printf("Error getting the user: %v\n", err)
}

4. Issues

For known issues, see issues.

5. Contributions

The Microsoft Graph SDK is open for contribution. To contribute to this project, see Contributing.

6. License

Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT license.

7. Third-party notices

Third-party notices

Issues
  • examples needed

    examples needed

    i'm new to go but after playing with this sdk for a bit, getting basic things to work took a long time and I gave up.

    I gave up on azidentity.NewClientCertificateCredential (does it need a fs path to a cert or a []*x509.Certificate?) I couldnt figure out how to set the right options for .IdentityProvidersRequestBuilderGetOption I couldn't figure out how to parse responses:

    result: &{{map[error:map[code:0xc000099c40 innerError:0xc0001bcf20 message:0xc000099d50]] <nil>} <nil>}
    result: &{map[error:map[code:0xc000012e60 innerError:0xc0000130e0 message:0xc000012f00]] <nil> []}
    cannot range over result (type *graph.IdentityProviderBase)
    cannot range over result2 (type *"github.com/microsoftgraph/msgraph-beta-sdk-go/identity/identityproviders".IdentityProvidersResponse)
    

    the only example I could find used result.GetId()) when calling Drive()

    Can we get an example of how to authenticate with a certificate, set options correctly, and work with responses?

    Your docs currently show this for most methods:

    //THE GO SDK IS IN PREVIEW. NON-PRODUCTION USE ONLY
    graphClient := msgraphsdk.NewGraphServiceClient(requestAdapter)
    result, err := graphClient.Identity().IdentityProviders().Get(options)
    

    what are the options? how do I parse the results?

    documentation needs author feedback status no recent activity 
    opened by maceip 17
  • Follow Go's commenting convetions

    Follow Go's commenting convetions

    Overview

    1. Follows Go commenting convention where comments didn't follow the convention
    2. Removes parameters and return types because go doc prints the function signature together with the documentation.

    Demo

    The snippet below shows the output of a go doc command. Note that the function signature is also printed followed by the comment description.

    package msgraphgocore // import "github.com/microsoftgraph/msgraph-sdk-go-core"
    
    func GetDefaultClient(middleware ...khttp.Middleware) *nethttp.Client
        GetDefaultClient creates a new http client with a preconfigured middleware
        pipeline
    
    opened by jobala 4
  • Filename is too long while cloning repository in windows

    Filename is too long while cloning repository in windows

    go get -u github.com/microsoftgraph/msgraph-sdk-go-core

    error: unable to create file msgraph-mail/java/utilities/src/main/java/graphjavav4/utilities/users/item/mailFolders/item/messages/item/multiValueExtendedProperties/item/MultiValueLegacyExtendedPropertyRequestBuilder.java: Filename too long error: unable to create file msgraph-mail/java/utilities/src/main/java/graphjavav4/utilities/users/item/mailFolders/item/messages/item/singleValueExtendedProperties/item/SingleValueLegacyExtendedPropertyRequestBuilder.java: Filename too long Unable to checkout 'b42e1a879cf62da7e666dcf38d4ab69357abba1d' in submodule path 'samples'****

    Please provide alternative way to do this in windows platform.

    question needs author feedback 
    opened by nagnagendra 3
  • ability to support search

    ability to support search

    I am using this code

    	searchStr := fmt.Sprintf("appId:%s", applicationId)
    	client := msgraphsdk.NewGraphServiceClient(adapter)
    	spResponse, err := client.ServicePrincipals().Get(&serviceprincipals.ServicePrincipalsRequestBuilderGetOptions{
    		Q: &serviceprincipals.ServicePrincipalsRequestBuilderGetQueryParameters{
    			Search: &searchStr,
    		},
    	})
    

    to attempt to generate a url like /v1.0/servicePrincipals?$search="appId:applicationId" however inside microsoft.kiota.abstractions/request_information.go it appears to use reflection to determine the fieldName. The end result of the RawQuery in the request object is search=appId%3Aca1ce348-e18e-4aa0-93fd-2901f9fc6ee8 w/o the dollar.

    is there a workaround for this? am I using the API incorrectly?

    I realize this is probably more about kiota and less about this SDK but would appreciate some comments or direction on this issue. Thanks!

    question 
    opened by chris-annino-sp 3
  • Ft page iterator #13

    Ft page iterator #13

    Overview

    Adds PageIterator

    Demo

    Create a page iterator task

    pageIterator, err := msgraphgocore.NewPageIterator(graphResponse, *reqAdapter, ParsableCons)
    pageIterator.SetHeaders(map[string]string{"Content-Type": "application/json"})
    

    Iterate

    err := pageIterator.Iterate(func(pageItem interface{}) bool {
          item := pageItem.(graph.User)
          res = append(res, *item.GetId())
          return true
    })
    
    

    Pause & Resume

    pageIterator.Iterate(func(pageItem interface{}) bool {
          item := pageItem.(graph.User)
          res = append(res, *item.GetId())
          
          return *item.GetId() != "2" // pauses when id == 2
    })
    
    // resumes iteration from user with id 3
    pageIterator.Iterate(func(pageItem interface{}) bool {
          item := pageItem.(graph.User)
          res2 = append(res2, *item.GetId())
          
          return true
    })
    
    

    Notes

    • I made the Next & HasNext methods private because we have not reached a consensus on making them part of the Iterator's API

    Testing

    • Run go test

    Closes #13

    opened by jobala 3
  • missing public documentation articles

    missing public documentation articles

    These sections of the readme point to public documentation sections that do not exist today getting started choosing your authentication provider

    TODO:

    • [ ] create the section in the public documentation (mark as preview)
    • [ ] update the readme

    @jasonjoh can I let you take this one on please?

    opened by baywet 3
  • Bump github.com/stretchr/testify from 1.7.2 to 1.7.5

    Bump github.com/stretchr/testify from 1.7.2 to 1.7.5

    Bumps github.com/stretchr/testify from 1.7.2 to 1.7.5.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    opened by dependabot[bot] 2
  • Bump github.com/stretchr/testify from 1.7.2 to 1.7.4

    Bump github.com/stretchr/testify from 1.7.2 to 1.7.4

    Bumps github.com/stretchr/testify from 1.7.2 to 1.7.4.

    Commits
    • 48391ba Fix panic in AssertExpectations for mocks without expectations (#1207)
    • 840cb80 arrays value types in a zero-initialized state are considered empty (#1126)
    • 07dc7ee Bump actions/setup-go from 3.1.0 to 3.2.0 (#1191)
    • c33fc8d Bump actions/checkout from 2 to 3 (#1163)
    • 3c33e07 Added Go 1.18.1 as a build/supported version (#1182)
    • e2b56b3 Bump github.com/stretchr/objx from 0.1.0 to 0.4.0
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    opened by dependabot[bot] 2
  • Bump github.com/stretchr/testify from 1.7.2 to 1.7.3

    Bump github.com/stretchr/testify from 1.7.2 to 1.7.3

    Bumps github.com/stretchr/testify from 1.7.2 to 1.7.3.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    opened by dependabot[bot] 2
  • Bump github.com/microsoft/kiota-abstractions-go from 0.4.0 to 0.5.0

    Bump github.com/microsoft/kiota-abstractions-go from 0.4.0 to 0.5.0

    Bumps github.com/microsoft/kiota-abstractions-go from 0.4.0 to 0.5.0.

    Release notes

    Sourced from github.com/microsoft/kiota-abstractions-go's releases.

    v0.5.0

    Added

    • Added vanity methods to request options to add headers and options to simplify code generation.
    Changelog

    Sourced from github.com/microsoft/kiota-abstractions-go's changelog.

    [0.5.0] - 2022-04-21

    Added

    • Added vanity methods to request options to add headers and options to simplify code generation.
    Commits
    • dc0939d Merge pull request #7 from microsoft/feature/request-options
    • de901fb - adds vanity methods to simplify generation for request configuration
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    opened by dependabot[bot] 2
  • Bump github.com/microsoft/kiota-serialization-json-go from 0.1.0 to 0.2.0

    Bump github.com/microsoft/kiota-serialization-json-go from 0.1.0 to 0.2.0

    Bumps github.com/microsoft/kiota-serialization-json-go from 0.1.0 to 0.2.0.

    Release notes

    Sourced from github.com/microsoft/kiota-serialization-json-go's releases.

    v0.2.0

    Changed

    • Breaking: simplifies the field deserializers.
    Changelog

    Sourced from github.com/microsoft/kiota-serialization-json-go's changelog.

    [0.2.0] - 2022-04-04

    Changed

    • Breaking: simplifies the field deserializers.
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    opened by dependabot[bot] 2
  • Change Notification models are missing

    Change Notification models are missing

    The Java SDK has models for ChangeNotification and ChangeNotificationCollection to Unmarshal into when handling webhook notifications from Subscriptions, but these and other associated models seem to be missing from the Go SDK.

    Is this possibly because of how the Go SDK is autogenerated that these are missing because they are not part of an API Request, but on the receiving end of data?

    enhancement help wanted 
    opened by nshttpd 1
  • simplify client initialization

    simplify client initialization

    here are the steps to get a client today

    auth, err := a.NewAzureIdentityAuthenticationProviderWithScopes(credentials, []string{"Files.Read"})
    // handle error
    adapter, err := msgraphsdk.NewGraphRequestAdapter(auth)
    //handle error
    client := msgraphsdk.NewGraphServiceClient(adapter)
    result, err := client.Me().Drive().Get(nil)
    

    Which makes for a lot of boiler plate code.

    By using inheritance, renaming the generated graph service client and adding an override constructor we could make it

    client := msgraphsdk.NewGraphServiceClientWithCredentials(credentials, []string{"Files.Read"})
    // panic if we get an error from the encapsulated layers.
    result, err := client.Me().Drive().Get(nil)
    
    enhancement help wanted 
    opened by baywet 0
  • investigate why the go doc is not available for models

    investigate why the go doc is not available for models

    https://pkg.go.dev/github.com/microsoftgraph/msgraph-sdk-go/models shows "Documentation is too large to display." when https://pkg.go.dev/github.com/microsoftgraph/msgraph-sdk-go#GraphServiceClient.Admin works fine.

    Originally reported in https://github.com/microsoftgraph/msgraph-sdk-go/issues/129

    bug documentation needs triage :mag: 
    opened by baywet 1
  • Ft batch requests #11

    Ft batch requests #11

    Overview

    Brief description of what this PR does

    Demo

    Optional. Screenshots, examples, etc.

    Notes

    Optional. Ancilliary topics, caveats, alternative strategies that didn't work out, anything else.

    Testing

    • How to test this PR
    • Prefer bulleted description
    • Start after checking out this branch
    • Include any setup required, such as bundling scripts, restarting services, etc.
    • Include test case and expected output
    opened by jobala 0
  • build time improvements

    build time improvements

    The build time of the service SDKs can take a while for the initial build/dependencies upgrade on some machines. This is caused by the breadth of our API but we might have opportunities to improve that aspect by reducing the amount of code we generate.

    The get field deserializers method might have opportunities for reduction by moving some of the code into helper methods in abstractions https://github.com/microsoftgraph/msgraph-sdk-go/blob/3a362de6cb79ffc639841e0d8fb122975173c145/models/microsoft/graph/message.go#L320

    Same for the serialize method https://github.com/microsoftgraph/msgraph-sdk-go/blob/3a362de6cb79ffc639841e0d8fb122975173c145/models/microsoft/graph/message.go#L667

    Lastly the field accessors might also provide such opportunity https://github.com/microsoftgraph/msgraph-sdk-go/blob/3a362de6cb79ffc639841e0d8fb122975173c145/models/microsoft/graph/message.go#L160

    The goal here is to look into these patterns, identify and share the examples of what could be improved, so we can feed the information back to the kiota generation process.

    enhancement 
    opened by baywet 0
Releases(v0.26.1)
Owner
Microsoft Graph
Unified endpoint for accessing data, relationships and insights coming from the Microsoft cloud
Microsoft Graph
Fluent JavaScript API for SharePoint and Microsoft Graph REST APIs

PnPjs is a fluent JavaScript API for consuming SharePoint and Microsoft Graph REST APIs in a type-safe way. You can use it with SharePoint Framework,

Microsoft 365 Community 595 Jun 21, 2022
Microsoft Azure SDK for Go

Azure SDK for Go This repository is for active development of the Azure SDK for Go. For consumers of the SDK you can follow the links below to visit t

Microsoft Azure 1.1k Jun 27, 2022
A Facebook Graph API SDK For Go.

A Facebook Graph API SDK In Golang This is a Go package that fully supports the Facebook Graph API with file upload, batch request and marketing API.

Huan Du 1.1k Jun 27, 2022
A go sdk for baidu netdisk open platform 百度网盘开放平台 Go SDK

Pan Go Sdk 该代码库为百度网盘开放平台Go语言的SDK

Jsyz Chen 71 Jun 15, 2022
Nextengine-sdk-go: the NextEngine SDK for the Go programming language

NextEngine SDK for Go nextengine-sdk-go is the NextEngine SDK for the Go programming language. Getting Started Install go get github.com/takaaki-s/nex

null 0 Dec 7, 2021
Commercetools-go-sdk is fork of original commercetools-go-sdk

commercetools-go-sdk The Commercetools Go SDK is automatically generated based on the official API specifications of Commercetools. It should therefor

Flink 0 Dec 13, 2021
Sdk-go - Go version of the Synapse SDK

synapsesdk-go Synapse Protocol's Go SDK. Currently in super duper alpha, do not

null 0 Jan 7, 2022
Redash-go-sdk - An SDK for the programmatic management of Redash, in Go

Redash Go SDK An SDK for the programmatic management of Redash. The main compone

RecoLabs 24 Mar 3, 2022
null 88 Jun 15, 2022
A Golang Core api of crane

Core API of Crane core api of crane. DEV GUIDE clone the project to your $GOPATH. following command will generate crd yamls and files in the project d

Crane 12 May 9, 2022
The core API for the Atlas Revision project

Atlas Revision API This is the core API for the Atlas Revision project. It provides question data for the website. All questions are from CIE. Initial

Thisas 2 Feb 16, 2022
Graph Role-Based Access Control by Animeshon

gRBAC - Graph Role-Based Access Control A cloud-native graph implementation of the Role-Based Access Control (RBAC) authorization architecture powered

gRBAC 16 May 11, 2022
Simple no frills AWS S3 Golang Library using REST with V4 Signing (without AWS Go SDK)

simples3 : Simple no frills AWS S3 Library using REST with V4 Signing Overview SimpleS3 is a golang library for uploading and deleting objects on S3 b

Rohan Verma 87 Jun 10, 2022
Go SDK library for the Solana Blockchain

Solana SDK library for Go Go library to interface with Solana JSON RPC and WebSocket interfaces. Clients for Solana native programs, Solana Program Li

Slavomir 321 Jun 24, 2022
A Golang Client Library for building Cosmos SDK chain clients

Cosmos Client Lib in Go This is the start of ideas around how to implement the cosmos client libraries in a seperate repo How to instantiate and use t

Jack Zampolin 113 Jun 18, 2022
AWS SDK for the Go programming language.

AWS SDK for Go aws-sdk-go is the official AWS SDK for the Go programming language. Checkout our release notes for information about the latest bug fix

Amazon Web Services 7.7k Jun 22, 2022
A Golang SDK for Medium's OAuth2 API

Medium SDK for Go This repository contains the open source SDK for integrating Medium's OAuth2 API into your Go app. Install go get github.com/Medium/

Medium 131 May 18, 2022
MinIO Client SDK for Go

MinIO Go Client SDK for Amazon S3 Compatible Cloud Storage The MinIO Go Client SDK provides simple APIs to access any Amazon S3 compatible object stor

High Performance, Kubernetes Native Object Storage 1.6k Jun 30, 2022
Twilight is an unofficial Golang SDK for Twilio APIs

Twilight is an unofficial Golang SDK for Twilio APIs. Twilight was born as a result of my inability to spell Twilio correctly. I searched for a Twillio Golang client library and couldn’t find any, I decided to build one. Halfway through building this, I realized I had spelled Twilio as Twillio when searching for a client library on Github.

Ghvst Code 18 Jul 2, 2021