Skip to content

Commit

Permalink
Merge pull request #2835 from bcameron1231/v4-TeamsAppCatalog
Browse files Browse the repository at this point in the history
V4 teams app catalog
  • Loading branch information
juliemturner authored Dec 4, 2023
2 parents bc8d1fc + db78afa commit 66fdc2b
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 1 deletion.
121 changes: 121 additions & 0 deletions docs/graph/appCatalog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# @pnp/graph/appcatalog

The ability to use Teams App Catalog

## AppCatalog, IAppCatalog

[![Invokable Banner](https://img.shields.io/badge/Invokable-informational.svg)](../concepts/invokable.md) [![Selective Imports Banner](https://img.shields.io/badge/Selective%20Imports-informational.svg)](../concepts/selective-imports.md)

## Get Teams Apps in App Catalog

Using teamsApps() you get the Teams AppCatalog

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);

const apps = await graph.appCatalog.teamsApps();

```
## Get Teams Apps by Id

Using getById() you get the Teams App by Id

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);

const apps = await graph.appCatalog.teamsApps.getById('{teams app id}')();

```
## Add a Teams App


```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);
const appPackage = {...} as Blob;

//second parameter is "Requires Approval"
const app = await graph.appCatalog.teamsApps.getById('{teams app id}').add(appPackage, false);

```
## Update a Teams App

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);
const appPackage = {...} as Blob;

//second parameter is "Requires Approval"
const app = await graph.appCatalog.teamsApps.getById('{teams app id}').update(appPackage, false);

```
## Delete a Teams App

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);

//delete a Teams App
await graph.appCatalog.teamsApps.getById(app).delete();

// delete an un-approved Teams App requires the app definition id.
// sample is just selecting the first app definition.
const appDefinition = (await graph.appCatalog.teamsApps.getById("{teams app id}")()).appDefinitions[0];
await graph.appCatalog.teamsApps.getById(app).delete(appDefinition);

```
## Get Teams App Definitions

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);

//get teams app definitions
await graph.appCatalog.teamsApps.getById(`{teams app id}`).appDefinitions();

```
## Get Teams App Definitions by Id

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);

//get teams app definitions
await graph.appCatalog.teamsApps.getById(`{teams app id}`).appDefinitions.getById(`{Teams App Definition Id}`)

```
## Get Bot associated with Teams App Definition

```TypeScript
import { graphfi } from "@pnp/graph";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";

const graph = graphfi(...);

await graph.appCatalog.teamsApps.getById(`{teams app id}`).appDefinitions.getById(`{Teams App Definition Id}`).bot();

```
22 changes: 22 additions & 0 deletions packages/graph/appCatalog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GraphFI } from "../fi.js";
import { AppCatalog, IAppCatalog} from "./types.js";


export {
AppCatalog,
IAppCatalog,
} from "./types.js";

declare module "../fi" {
interface GraphFI {
readonly appCatalog: IAppCatalog;
}
}

Reflect.defineProperty(GraphFI.prototype, "appCatalog", {
configurable: true,
enumerable: true,
get: function (this: GraphFI) {
return this.create(AppCatalog);
},
});
122 changes: 122 additions & 0 deletions packages/graph/appCatalog/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
AppCatalogs as IAppCatalogsType,
TeamsApp as ITeamsAppType,
TeamsAppDefinition as ITeamsAppDefinitionType,
TeamworkBot as ITeamworkBot } from "@microsoft/microsoft-graph-types";
import { _GraphCollection, graphInvokableFactory, _GraphInstance, graphPost, graphDelete, graphGet } from "../graphqueryable.js";
import { IGetById, defaultPath, getById } from "../decorators.js";
import { InjectHeaders } from "@pnp/queryable/index.js";

/**
* AppCatalogs
*/

@defaultPath("appCatalogs")
export class _AppCatalog extends _GraphInstance<IAppCatalogsType> {
/**
* Get teams apps in appCatalog
*
*/
public get teamsApps(): ITeamsApps {
return TeamsApps(this);
}

}
export interface IAppCatalog extends _AppCatalog {}
export const AppCatalog = graphInvokableFactory<IAppCatalog>(_AppCatalog);

/**
* AppDefinition
*/
export class _AppDefinition extends _GraphInstance<ITeamsAppDefinitionType> {
/**
* Gets bot associated with app
*
*/
public async bot(): Promise<ITeamworkBot>{
return graphGet(AppDefinitions(this, "/bot"));
}
}
export interface IAppDefinition extends _AppDefinition { }
export const AppDefinition = graphInvokableFactory<IAppDefinition>(_AppDefinition);

/**
* AppDefinitions
*/

@defaultPath("appDefinitions")
@getById(AppDefinition)
export class _AppDefinitions extends _GraphCollection<ITeamsAppDefinitionType[]> {}
export interface IAppDefinitions extends _AppDefinitions, IGetById<IAppDefinition> {}
export const AppDefinitions = graphInvokableFactory<IAppDefinitions>(_AppDefinitions);


/**
* TeamsApp
*/
export class _TeamsApp extends _GraphInstance<ITeamsAppType> {
/**
* Get app definitions
*
*/
public get appDefinitions(): IAppDefinitions {
return AppDefinitions(this);
}

/**
* Deletes a Teams App
*
*/
public async delete(appDefinitionId?: string): Promise<any> {
// Un-approved apps must be deleted differently. https://learn.microsoft.com/en-us/graph/api/teamsapp-delete?view=graph-rest-1.0&tabs=http#permissions
if(appDefinitionId){
return graphDelete(AppDefinitions(this,`/${appDefinitionId}`));
}
return graphDelete(this);
}

/**
* Updates a Teams App
*
* @param zip zip file of app
* @param requiresReview This optional query parameter triggers the app review process. Users with admin privileges can submit apps without triggering a review.
*/
public async update(zip: Blob, requiresReview = false): Promise<ITeamsAppType> {
const q = AppDefinitions(this,`?$requiresReview=${requiresReview}`);
q.using(InjectHeaders({
"Content-Type": "application/zip",
}));

return graphPost(q, { body: zip });
}
}

export interface ITeamsApp extends _TeamsApp{}
export const TeamsApp = graphInvokableFactory<ITeamsApp>(_TeamsApp);


/**
* TeamsApps
*/

@defaultPath("teamsApps")
@getById(TeamsApp)
export class _TeamsApps extends _GraphCollection<ITeamsAppType[]> {
/**
* Adds a Teams App
*
* @param zip zip file of app
* @param requiresReview This optional query parameter triggers the app review process. Users with admin privileges can submit apps without triggering a review.
*
*/
public async add(zip: Blob, requiresReview = false): Promise<ITeamsAppType> {
const q = TeamsApp(this, `?requiresReview=${requiresReview}`);
q.using(InjectHeaders({
"Content-Type": "application/zip",
}));

return graphPost(q, { body: zip });
}
}
export interface ITeamsApps extends _TeamsApps, IGetById<ITeamsApp>{}
export const TeamsApps = graphInvokableFactory<ITeamsApps>(_TeamsApps);
2 changes: 2 additions & 0 deletions packages/graph/presets/all.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "../appCatalog/index.js";
import "../attachments/index.js";
import "../calendars/index.js";
import "../cloud-communications/index.js";
Expand All @@ -20,6 +21,7 @@ import "../subscriptions/index.js";
import "../teams/index.js";
import "../users/index.js";

export * from "../appCatalog/index.js";
export * from "../attachments/index.js";
export * from "../calendars/index.js";
export * from "../cloud-communications/index.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/graph/teams/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export interface IMessage extends _Message { }
export const Message = graphInvokableFactory<IMessage>(_Message);

/**
* Channels
* Messages
*/
@defaultPath("messages")
@getById(Message)
Expand Down
47 changes: 47 additions & 0 deletions test/graph/appCatalogs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { expect } from "chai";
import "@pnp/graph/teams";
import "@pnp/graph/appCatalog";
import { pnpTest } from "../pnp-test.js";

describe.only("AppCatalog", function () {

before(async function () {

if (!this.pnp.settings.enableWebTests) {
this.skip();
}
});

it("teamsApps", pnpTest("32d84a70-52cb-47c8-8957-cda902c07d85", async function () {
const apps = await this.pnp.graph.appCatalog.teamsApps();
return expect(apps).to.be.an("array") && expect(apps[0]).to.haveOwnProperty("id");
}));

it("teamsApps - getById()", pnpTest("17bfb2cd-8fd3-41d3-a387-2fcf410b7100", async function () {
let passed = false;
const apps = await this.pnp.graph.appCatalog.teamsApps();
if (apps.length > 0) {
const app = await this.pnp.graph.appCatalog.teamsApps.getById(apps[0].id)();
passed = (app.id === apps[0].id);
}
return expect(passed).is.true;
}));

it("appDefinitions", pnpTest("63c8ef41-067f-4f58-bd78-9b5d8d60b5b4", async function () {
const apps = await this.pnp.graph.appCatalog.teamsApps();
const appDefinitions = await this.pnp.graph.appCatalog.teamsApps.getById(apps[0].id).appDefinitions();
return expect(appDefinitions).to.be.an("array") && expect(appDefinitions[0]).to.haveOwnProperty("id");
}));

it("appDefinitions - getById()", pnpTest("11dce742-2aeb-4b8e-8967-6f73b7fd55d6", async function () {
let passed = false;
const apps = await this.pnp.graph.appCatalog.teamsApps();
const appDefinitions = await this.pnp.graph.appCatalog.teamsApps.getById(apps[0].id).appDefinitions();

if (apps.length > 0) {
const def = await this.pnp.graph.appCatalog.teamsApps.getById(apps[0].id).appDefinitions.getById(appDefinitions[0].id)();
passed = (def.id === appDefinitions[0].id);
}
return expect(passed).is.true;
}));
});

0 comments on commit 66fdc2b

Please sign in to comment.