Skip to content

Commit

Permalink
Lock on change, fixes, refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
marcellourbani committed Dec 10, 2018
1 parent b518a04 commit 2472f40
Show file tree
Hide file tree
Showing 30 changed files with 429 additions and 173 deletions.
25 changes: 13 additions & 12 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"javascript.format.enable": false,
"typescript.locale": "en",
"gitlens.codeLens.enabled": false
}
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"javascript.format.enable": false,
"typescript.locale": "en",
"gitlens.codeLens.enabled": false,
"editor.fontLigatures": true
}
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# ABAP remote filesystem for visual studio code

This extension allows editing and activation of ABAP code on your server directly in Visual studio code.
Doesn't allow to create objects yet, but does read/save/activate several object types
This extension allows editing and activation of ABAP code on your server directly in Visual studio code, including transport assignment and creation (if your system supports it).

**Unless your system is very modern, write support will require you to install [this extension](https://github.com/marcellourbani/abapfs_extensions)** in your dev server to enable locking files

**WRITE SUPPORT IS EXPERIMANTAL USE AT YOUR OWN RISK**
**THIS SOFTWARE IS IN BETA TEST, USE AT YOUR OWN RISK**

![anim](https://user-images.githubusercontent.com/2453277/47482169-ae0cc300-d82d-11e8-8d19-f55dd877c166.gif)
![image](https://user-images.githubusercontent.com/2453277/47466602-dd99dc00-d7e9-11e8-97ed-28e23dfd8f90.png)
Expand All @@ -21,16 +20,16 @@ The complete list of editable objects depends on your installation, on my local
- programs/includes
- function groups
- classes
- transformations
- transformations (except creation)

![anim](https://user-images.githubusercontent.com/2453277/48232926-30a78d80-e3ab-11e8-8a12-00844431f9af.gif)

## setup

Too early to publish as an extension, there's a compiled extension you can run from source or install from the command line with
Will soon be published in the marketplace, in the meanwhile there's a compiled extension you can run from source or install from the command line with

```shell
code --install-extension vscode-abap-remote-fs-0.1.0.vsix
code --install-extension vscode-abap-remote-fs-0.3.0.vsix
```

The compiled file can be either downloaded from for the
Expand Down
Binary file added images/abapfs_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
"name": "vscode-abap-remote-fs",
"displayName": "vscode_abap_remote_fs",
"description": "Work on your ABAP code straight from the server",
"version": "0.2.0",
"version": "0.3.0",
"publisher": "murbani",
"license": "MIT",
"icon": "images/abapfs_icon.png",
"author": {
"email": "marcello.urbani@gmail.com",
"name": "Marcello Urbani"
Expand Down Expand Up @@ -43,12 +44,12 @@
"tsconfig-paths": "^3.7.0",
"tslint": "^5.8.0",
"typescript": "^2.6.1",
"vsce": "^1.52.0",
"vscode": "^1.1.21"
"vsce": "^1.53.2",
"vscode": "^1.1.26"
},
"dependencies": {
"request": "^2.88.0",
"event-stream": "3.3.4",
"request": "^2.88.0",
"xml2js": "^0.4.19"
},
"activationEvents": [
Expand Down
31 changes: 28 additions & 3 deletions src/adt/AdtConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,24 @@ enum ConnStatus {
active,
failed
}
export interface StateRequestor {
needStateFul: boolean
}
export class AdtConnection {
readonly name: string
readonly url: string
readonly username: string
readonly password: string
//TODO: proper session support
stateful = true

get stateful() {
for (const r of this._stateRequestors) if (r.needStateFul) return true
return false
}
private _csrftoken: string = FETCH_CSRF_TOKEN
private _status: ConnStatus = ConnStatus.new
private _listeners: Array<Function> = []
private _clone?: AdtConnection
private _stateRequestors: Set<StateRequestor> = new Set()

constructor(name: string, url: string, username: string, password: string) {
this.name = name
Expand All @@ -44,12 +51,15 @@ export class AdtConnection {
this.username,
this.password
)
this._clone.stateful = false
}
await this._clone.connect()
return this._clone
}

addStateRequestor(r: StateRequestor) {
this._stateRequestors.add(r)
}

isActive(): boolean {
return this._status === ConnStatus.active
}
Expand Down Expand Up @@ -138,6 +148,21 @@ export class AdtConnection {
query
})
}

dropSession() {
return this.myrequest(
"/sap/bc/adt/repository/informationsystem/objecttypes",
"GET",
{
headers: {
"x-csrf-token": this._csrftoken,
"X-sap-adt-sessiontype": "",
Accept: "*/*"
}
}
)
}

connect(): Promise<request.Response> {
return this.myrequest(
"/sap/bc/adt/repository/informationsystem/objecttypes?maxItemCount=999&name=*&data=usedByProvider"
Expand Down
8 changes: 6 additions & 2 deletions src/adt/AdtExceptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { parsetoPromise, getFieldAttribute, recxml2js } from "./AdtParserBase"
import {
parseToPromise,
getFieldAttribute,
recxml2js
} from "./parsers/AdtParserBase"
import { Response } from "request"
const TYPEID = Symbol()

Expand All @@ -23,7 +27,7 @@ export class AdtException extends Error {
}

static async fromXml(xml: string): Promise<AdtException> {
const raw: any = await parsetoPromise()(xml)
const raw: any = await parseToPromise()(xml)
const root: any = raw["exc:exception"]
const namespace = getFieldAttribute("namespace", "id", root)
const type = getFieldAttribute("type", "id", root)
Expand Down
53 changes: 36 additions & 17 deletions src/adt/AdtServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { AdtConnection } from "./AdtConnection"
import { Uri, FileSystemError, FileType, commands } from "vscode"
import { MetaFolder } from "../fs/MetaFolder"
import { AbapObjectNode, AbapNode, isAbapNode } from "../fs/AbapNode"
import { AbapObject, TransportStatus, isAbapObject } from "../abap/AbapObject"
import { AbapObject, TransportStatus, isAbapObject } from "./abap/AbapObject"
import { getRemoteList } from "../config"
import { selectTransport } from "./AdtTransports"
import { AdtObjectActivator } from "./AdtObjectActivator"
import { AdtObjectActivator } from "./operations/AdtObjectActivator"
import { pick } from "../functions"
import { AdtObjectFinder } from "./AdtObjectFinder"
import { AdtObjectCreator } from "./create/AdtObjectCreator"
import { PACKAGE } from "./create/AdtObjectTypes"
import { AdtObjectFinder } from "./operations/AdtObjectFinder"
import { AdtObjectCreator } from "./operations/AdtObjectCreator"
import { PACKAGE } from "./operations/AdtObjectTypes"
import { LockManager } from "./operations/LockManager"
import { AdtException } from "./AdtExceptions"
export const ADTBASEURL = "/sap/bc/adt/repository/nodestructure"

/**
Expand All @@ -20,7 +22,7 @@ export const ADTBASEURL = "/sap/bc/adt/repository/nodestructure"
const uriParts = (uri: Uri): string[] =>
uri.path
.split("/")
.filter((v, idx, arr) => (idx > 0 && idx < arr.length - 1) || v) //ignore empty at begginning or end
.filter((v, idx, arr) => (idx > 0 && idx < arr.length - 1) || v) //ignore empty at beginning or end
/**
* centralizes most API accesses
* some will be delegated/provided from members or ABAP object nodes
Expand All @@ -30,7 +32,8 @@ export class AdtServer {
private readonly activator: AdtObjectActivator
readonly root: MetaFolder
readonly objectFinder: AdtObjectFinder
creator: AdtObjectCreator
readonly creator: AdtObjectCreator
readonly lockManager: LockManager
private lastRefreshed?: string

/**
Expand All @@ -50,6 +53,7 @@ export class AdtServer {
this.creator = new AdtObjectCreator(this)
this.activator = new AdtObjectActivator(this.connection)
this.objectFinder = new AdtObjectFinder(this.connection)
this.lockManager = new LockManager(this.connection)
this.connection
.connect()
.then(pick("body"))
Expand Down Expand Up @@ -109,32 +113,40 @@ export class AdtServer {
if (!isAbapNode(file))
throw FileSystemError.NoPermissions("Can only save source code")

await file.abapObject.lock(this.connection)
if (file.abapObject.transport === TransportStatus.REQUIRED) {
const obj = file.abapObject
//check file is locked
if (!this.lockManager.isLocked(obj))
throw new AdtException(
"lockNotFound",
`Object not locked ${obj.type} ${obj.name}`
)

if (obj.transport === TransportStatus.REQUIRED) {
const transport = await selectTransport(
file.abapObject.getContentsUri(this.connection),
obj.getContentsUri(this.connection),
"",
this.connection
)
if (transport) file.abapObject.transport = transport
}

await file.abapObject.setContents(this.connection, content)
const lockId = this.lockManager.getLockId(obj)
await obj.setContents(this.connection, content, lockId)

await file.abapObject.unlock(this.connection)
await file.stat(this.connection)
await this.lockManager.unlock(obj)
//might have a race condition with user changing editor...
commands.executeCommand("setContext", "abapfs:objectInactive", true)
}

/**
* converts vscode URI to ADT URI
* @see findNodeHierarcy for more details
* @see findNodeHierarchy for more details
*
* @param uri vscode URI
*/
findNode(uri: Uri): AbapNode {
return this.findNodeHierarcy(uri)[0]
return this.findNodeHierarchy(uri)[0]
}

/**
Expand All @@ -145,7 +157,7 @@ export class AdtServer {
* @abstract visual studio paths are hierarchic, adt ones aren't
* so we need a way to translate the hierarchic ones to the original ones
* this file is concerned with telling whether a path is a real ADT one or one from vscode
* /sap/bc/adt/repository/nodestructure (with ampty query) is the root of both
* /sap/bc/adt/repository/nodestructure (with empty query) is the root of both
* also, several objects have namespaces.
* Class /foo/bar of package /foo/baz in code will have a path like
* /sap/bc/adt/repository/nodestructure/foo/baz/foo/bar
Expand All @@ -155,7 +167,7 @@ export class AdtServer {
*
* @param uri VSCode URI
*/
findNodeHierarcy(uri: Uri): AbapNode[] {
findNodeHierarchy(uri: Uri): AbapNode[] {
const parts = uriParts(uri)
return parts.reduce(
(current: AbapNode[], name) => {
Expand Down Expand Up @@ -185,7 +197,7 @@ export class AdtServer {
for (const part of parts) {
let next: AbapNode | undefined = node.getChild(part)
if (!next && refreshable) {
//refreshable will tipically be the current node or its first abap parent (usually a package)
//refreshable will typically be the current node or its first abap parent (usually a package)
await refreshable.refresh(this.connection)
next = node.getChild(part)
}
Expand Down Expand Up @@ -259,3 +271,10 @@ export const fromUri = (uri: Uri) => {
if (uri.scheme === "adt") return getServer(uri.authority)
throw FileSystemError.FileNotFound(uri)
}
export async function disconnect() {
const promises: Promise<any>[] = []
for (const server of servers) {
promises.push(server[1].connection.dropSession())
}
await Promise.all(promises)
}
6 changes: 3 additions & 3 deletions src/adt/AdtTransports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JSON2AbapXML } from "../abap/JSONToAbapXml"
import { parsetoPromise, getNode, recxml2js } from "./AdtParserBase"
import { JSON2AbapXML } from "./abap/JSONToAbapXml"
import { parseToPromise, getNode, recxml2js } from "./parsers/AdtParserBase"
import { mapWith, flat } from "../functions"
import { AdtConnection } from "./AdtConnection"
import { window, Uri } from "vscode"
Expand Down Expand Up @@ -84,7 +84,7 @@ export async function getTransportCandidates(
})
}
)
const rawdata = await parsetoPromise()(response.body)
const rawdata = await parseToPromise()(response.body)
const header = getNode(
"asx:abap/asx:values/DATA",
mapWith(recxml2js),
Expand Down
Loading

0 comments on commit 2472f40

Please sign in to comment.